<?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: Wallace Espindola</title>
    <description>The latest articles on DEV Community by Wallace Espindola (@wallaceespindola).</description>
    <link>https://dev.to/wallaceespindola</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%2F1257381%2Fb7a2e040-bb39-4215-a6ff-b54c5f1bdbf5.jpg</url>
      <title>DEV Community: Wallace Espindola</title>
      <link>https://dev.to/wallaceespindola</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wallaceespindola"/>
    <language>en</language>
    <item>
      <title>Python Just Turned 35, Here's What Kept It Alive All These Years</title>
      <dc:creator>Wallace Espindola</dc:creator>
      <pubDate>Fri, 20 Feb 2026 21:52:01 +0000</pubDate>
      <link>https://dev.to/wallaceespindola/python-just-turned-35-heres-what-kept-it-alive-all-these-years-4jh9</link>
      <guid>https://dev.to/wallaceespindola/python-just-turned-35-heres-what-kept-it-alive-all-these-years-4jh9</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%2Fu5ydco57fn4c3y6hjkif.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%2Fu5ydco57fn4c3y6hjkif.png" alt=" " width="800" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today is February 20, 2026. Exactly 35 years ago, Guido van Rossum released the first public version of Python, a language he'd been hacking on during the Christmas holidays as a side project. Let that sink in for a second. A side project from 1991 is now arguably the most popular programming language on the planet.&lt;/p&gt;

&lt;p&gt;I've been thinking about this milestone all week, and honestly, the more I dig into Python's story, the more impressed I get. Not because Python is perfect, it's definitely not, but because it kept finding ways to matter, decade after decade, even when the tech world shifted underneath it. That's rare. Languages come and go all the time. Most of them peak and fade within ten years. Python just... kept going.&lt;/p&gt;

&lt;p&gt;So let's talk about how a hobby project turned into the backbone of modern AI, web development, data science and pretty much everything in between.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Quick Trip Through Four Eras
&lt;/h2&gt;

&lt;p&gt;Python's 35-year history breaks down pretty neatly into four phases. Each one could've been the end of the road, but instead each one became a springboard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: The Scripting Era (1991-2000)
&lt;/h3&gt;

&lt;p&gt;Back in the early '90s, Python was "a better scripting language." That was the pitch. If you were writing shell scripts or wrestling with C for quick automation tasks, Python gave you something cleaner and more readable. It showed up in universities and Unix shops, quietly building a reputation as the language that didn't make you hate your life when you read someone else's code six months later.&lt;/p&gt;

&lt;p&gt;Was it glamorous? Not even close. But it was useful, and people kept coming back to it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: The Web Era (2000-2010)
&lt;/h3&gt;

&lt;p&gt;Then the web happened, and suddenly everybody needed a way to build websites that wasn't PHP spaghetti code. Ruby had Rails and that was the cool kid on the block for a while, but Python answered with Django in 2005 and Flask in 2010.&lt;/p&gt;

&lt;p&gt;Django gave you the full kitchen, ORM, admin panel, authentication, the works. Flask gave you a knife and a cutting board and said "go build whatever you want." Between the two of them, Python carved out real territory in web development. It wasn't the dominant web language, but it was a serious contender, and startups loved it for getting things shipped fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3: The Data Science Revolution (2010-2020)
&lt;/h3&gt;

&lt;p&gt;This is where things went nuclear. NumPy and SciPy had been around for a while, but when Pandas matured and Jupyter Notebooks made exploratory analysis actually pleasant, Python became the default language for anyone touching data. Then TensorFlow and PyTorch showed up, and suddenly Python wasn't just a data language, it was &lt;em&gt;the&lt;/em&gt; AI language.&lt;/p&gt;

&lt;p&gt;Researchers, data scientists and machine learning engineers all converged on Python. The network effects were enormous. If you wanted to do ML, you used Python. Period. The ecosystem was too deep, the community too large, the tooling too mature to ignore.&lt;/p&gt;

&lt;p&gt;Python's identity shifted from "solid web language" to "the language of data and AI." And that identity has only gotten stronger since.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 4: The Platform Era (2020-Present)
&lt;/h3&gt;

&lt;p&gt;Now we're in the era where Python is everywhere, and I mean &lt;em&gt;everywhere&lt;/em&gt;. FastAPI brought modern, async-first API development with automatic OpenAPI docs. Python runs DevOps pipelines, orchestrates cloud infrastructure, powers AI agents and serves as the glue language connecting systems across the entire stack.&lt;/p&gt;

&lt;p&gt;It's not just a language anymore, it's a universal integration platform. You can prototype an idea in the morning, deploy an API by lunch, hook it up to an AI model in the afternoon and automate the whole thing before dinner. That's the power of what Python has become.&lt;/p&gt;

&lt;h2&gt;
  
  
  So What Actually Kept Python Alive for 35 Years?
&lt;/h2&gt;

&lt;p&gt;Plenty of languages had momentum at some point. Perl was huge. Ruby was everywhere for a while. CoffeeScript had its moment. They all faded or shrank. Python didn't. Why?&lt;/p&gt;

&lt;h3&gt;
  
  
  Human-Centered Design
&lt;/h3&gt;

&lt;p&gt;This is the big one, and it goes back to Guido's original philosophy. Python optimized for the person reading the code, not the person writing it. That sounds like a small thing, but it's massive over time. Code is read far more often than it's written, and Python's insistence on readability meant that projects stayed maintainable longer, onboarding new developers was easier and collaboration just worked better.&lt;/p&gt;

&lt;p&gt;The famous "there should be one obvious way to do it" mantra meant less time arguing about style and more time building things.&lt;/p&gt;

&lt;h3&gt;
  
  
  Batteries Included
&lt;/h3&gt;

&lt;p&gt;Python's standard library was always surprisingly complete. Need to parse JSON? It's there. HTTP requests? There. CSV files, email, regex, unit testing, threading? All there. You could get a lot done before you ever needed to &lt;code&gt;pip install&lt;/code&gt; anything.&lt;/p&gt;

&lt;p&gt;This mattered enormously in the early days when package management was rough, and it still matters today when you just want to write a quick script without pulling in half the internet.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Ecosystem Flywheel
&lt;/h3&gt;

&lt;p&gt;Once Python hit critical mass in universities, the flywheel started spinning. Students learned Python, graduated, used Python at work, wrote libraries in Python, taught Python to the next generation. Researchers published papers with Python code. Startups built MVPs in Python. Every new library made the ecosystem richer, which attracted more developers, which produced more libraries.&lt;/p&gt;

&lt;p&gt;It's the classic network effect, and Python benefited from it more than almost any other language in history.&lt;/p&gt;

&lt;h3&gt;
  
  
  Low Barrier of Entry
&lt;/h3&gt;

&lt;p&gt;Python never stopped being approachable. You can teach a complete beginner to write meaningful Python code in a day. That constant influx of new developers kept the community fresh, energetic and growing. Languages that become too complex or too niche eventually stagnate. Python avoided that trap by always keeping the front door wide open.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adaptability
&lt;/h3&gt;

&lt;p&gt;This might be the most underrated factor. Python didn't fight paradigm shifts, it absorbed them. Web frameworks? Python adapted. Data science? Python adapted. Machine learning? Python adapted. Cloud-native microservices? Adapted again. Every time the industry moved, Python found a way to be relevant in the new landscape.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Strong Right Now
&lt;/h2&gt;

&lt;p&gt;Let's be honest about where Python genuinely excels today, because the list is impressive:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Developer productivity is unmatched.&lt;/strong&gt; For getting from idea to working code, nothing beats Python. The read-write-test cycle is incredibly fast, and the language stays out of your way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI and ML dominance is real.&lt;/strong&gt; PyTorch, TensorFlow, scikit-learn, Hugging Face, LangChain, the entire AI stack runs on Python. If you're doing anything with AI, you're writing Python or you're calling Python.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The ecosystem is massive.&lt;/strong&gt; PyPI has hundreds of thousands of packages. Whatever problem you're trying to solve, someone's probably already built a library for it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-domain reach.&lt;/strong&gt; Python is one of the very few languages where the same syntax works for web backends, data analysis, automation scripts, AI models and system administration. That versatility is genuinely rare.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prototyping speed.&lt;/strong&gt; When you need to validate an idea quickly, Python lets you move fast without accumulating too much technical debt in the prototype phase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Orchestration.&lt;/strong&gt; Python has become the default "glue language", the thing that ties together databases, APIs, AI models, cloud services and everything else. FastAPI made this even more compelling with its modern async approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Could Still Get Better
&lt;/h2&gt;

&lt;p&gt;I love Python, but I'm not going to pretend it's flawless. There are real pain points that the community has been wrestling with for years.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;p&gt;Let's get the elephant out of the room. Python is slow compared to compiled languages. It uses more memory and CPU time than Go, Rust, Java or C++. Yes, there's Cython, PyPy and the ongoing work on faster CPython. Yes, you can drop into C extensions for hot paths. But the baseline performance story is still a weakness, especially as workloads scale up.&lt;/p&gt;

&lt;p&gt;The good news is that the core team is actively working on this. The "Faster CPython" project has already delivered meaningful speedups in recent releases, and JIT compilation is on the roadmap. It won't match Rust anytime soon, but the gap is narrowing.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Packaging Mess
&lt;/h3&gt;

&lt;p&gt;Oh, the packaging. We've got pip, poetry, conda, pipenv, pdm, hatch and now uv. Each one has its fans, each one does things slightly differently, and none of them has emerged as the single obvious choice. Compare this to npm in JavaScript or cargo in Rust, where there's basically one answer, and you can see why this frustrates people.&lt;/p&gt;

&lt;p&gt;Yes, uv is making a strong case as the future here, it's fast, it's well-designed, and it's backed by the Astral team that gave us Ruff. But we're still in the "multiple competing standards" phase, and it creates real friction for teams and newcomers alike.&lt;/p&gt;

&lt;h3&gt;
  
  
  The GIL
&lt;/h3&gt;

&lt;p&gt;The Global Interpreter Lock has been Python's concurrency bottleneck for decades. It prevents true parallel execution of Python threads on multiple CPU cores. For I/O-bound work it doesn't matter much, but for CPU-intensive parallel processing it's a real limitation.&lt;/p&gt;

&lt;p&gt;The exciting development is PEP 703 and the experimental no-GIL builds. Python 3.13 shipped with an experimental free-threaded mode, and the community is cautiously optimistic. This could be a game-changer, but it'll take years for the ecosystem to fully adapt.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enterprise Governance
&lt;/h3&gt;

&lt;p&gt;Dynamic typing is great for speed but can be a headache at scale. Large Python codebases without disciplined use of type hints, linters and testing can become maintenance nightmares. Java and C# have built-in guardrails that Python relies on developers to self-impose.&lt;/p&gt;

&lt;p&gt;The gradual typing story with mypy, pyright and type hints has improved enormously, but it's still opt-in. In enterprise environments where dozens of teams touch the same codebase, that "opt-in" nature can be a real challenge.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Java Angle: Rivals or Partners?
&lt;/h2&gt;

&lt;p&gt;Java turned 30 last year. Python turns 35 today. And people still love asking "which one should I learn?" as if it's a zero-sum game. It's not.&lt;/p&gt;

&lt;p&gt;Here's the thing, Python and Java don't actually compete for the same jobs in most modern architectures. Java is the infrastructure backbone. It runs the banking systems, the telecom platforms, the enterprise middleware that needs to be rock-solid, handle massive concurrency and run for years without surprises. The JVM is a masterpiece of engineering, and Java's static typing and mature tooling make it ideal for large-scale systems where reliability matters more than development speed.&lt;/p&gt;

&lt;p&gt;Python is the intelligence layer. It runs the AI models, the data pipelines, the automation scripts and the rapid-prototype APIs. It's where experimentation happens, where new ideas get tested and where developers move fast.&lt;/p&gt;

&lt;p&gt;In practice, modern platforms often use both. Java handles the core transactional processing. Python handles the analytics, ML inference and orchestration. They complement each other beautifully, and arguing about which one is "better" misses the point entirely. They're optimized for different things, Java for systems, Python for humans.&lt;/p&gt;

&lt;p&gt;If you're early in your career, learn both. Seriously. Having Python and Java in your toolkit makes you valuable in almost any engineering organization.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Take on Python's Future
&lt;/h2&gt;

&lt;p&gt;I've been writing Python for years, alongside Java and a handful of other languages. Here's what I think the next decade looks like.&lt;/p&gt;

&lt;p&gt;Python isn't going anywhere. The AI wave alone guarantees its relevance for at least another ten years. But more than that, Python has demonstrated something powerful: the ability to reinvent itself without losing its soul. It's still readable, still approachable, still the language where you can teach a beginner in the morning and deploy production AI in the afternoon.&lt;/p&gt;

&lt;p&gt;The things that need fixing, performance, packaging, the GIL, are all actively being worked on. The community is engaged, the core team is focused and the commercial investment (from companies like Microsoft, Google and Meta) is substantial. Python's governance model, with its steering council and PEP process, has proven resilient and effective.&lt;/p&gt;

&lt;p&gt;My prediction? Python at 40 will be faster, better packaged, truly concurrent and even more deeply embedded in AI infrastructure. The language itself will probably look mostly the same, and that's the point. Python's superpower was never about being the most innovative syntax or the fastest runtime. It was about being the most &lt;em&gt;useful&lt;/em&gt; language for the most people, and that hasn't changed in 35 years.&lt;/p&gt;

&lt;p&gt;Happy birthday, Python. Here's to 35 more.&lt;/p&gt;




&lt;h2&gt;
  
  
  Let's Discuss
&lt;/h2&gt;

&lt;p&gt;I'd love to hear your perspective on Python's journey:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When did you first start using Python, and what was the first thing you built with it?&lt;/li&gt;
&lt;li&gt;What's your biggest frustration with Python today?&lt;/li&gt;
&lt;li&gt;Do you use Python alongside another language? How do they complement each other?&lt;/li&gt;
&lt;li&gt;Where do you think Python will be in 10 years?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop your thoughts in the comments. I'm genuinely curious what this community thinks.&lt;/p&gt;

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




&lt;p&gt;&lt;em&gt;Written by Wallace Espindola, Software Engineer and Python Enthusiast.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Connect with me:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://www.linkedin.com/in/wallaceespindola/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;&lt;a href="https://github.com/wallaceespindola/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>softwaredevelopment</category>
      <category>programming</category>
      <category>development</category>
    </item>
    <item>
      <title>Contract-First Integration: A Practical Guide for Developers</title>
      <dc:creator>Wallace Espindola</dc:creator>
      <pubDate>Tue, 10 Feb 2026 20:12:47 +0000</pubDate>
      <link>https://dev.to/wallaceespindola/contract-first-integration-a-practical-guide-for-developers-49nf</link>
      <guid>https://dev.to/wallaceespindola/contract-first-integration-a-practical-guide-for-developers-49nf</guid>
      <description>&lt;p&gt;Ever had that moment where you finish building an API, tell the frontend team "it's ready," and then spend two days debugging why their requests keep failing? Yeah, me too.&lt;/p&gt;

&lt;p&gt;Turns out there's a better way: Define the contract first, then build the code to match it. In this guide, I'll show you exactly how to do contract-first integration with Spring Boot, OpenAPI, and Kafka.&lt;/p&gt;

&lt;p&gt;No theory. Just practical steps you can use today.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Contract-First Integration?
&lt;/h2&gt;

&lt;p&gt;Simple version: You write the API specification before you write any code.&lt;/p&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Code the API&lt;/li&gt;
&lt;li&gt;Test it&lt;/li&gt;
&lt;li&gt;Document it&lt;/li&gt;
&lt;li&gt;Hope it matches what consumers expected&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write the contract (OpenAPI spec or Avro schema)&lt;/li&gt;
&lt;li&gt;Get everyone to agree on it&lt;/li&gt;
&lt;li&gt;Generate code from the contract&lt;/li&gt;
&lt;li&gt;Implement the logic&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The contract is the single source of truth. Code, tests, and docs all come from it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Should You Care?
&lt;/h2&gt;

&lt;p&gt;Three reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. No more "but I thought you meant..."&lt;/strong&gt; conversations&lt;/p&gt;

&lt;p&gt;When you define the contract upfront, there's no ambiguity. The spec says exactly what fields are required, what types they are, and what errors to expect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Teams can work in parallel&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Frontend doesn't have to wait for backend. They can generate a client SDK from the contract and develop against a mock&lt;br&gt;
server. When the real API is ready, they just switch the URL. No code changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Breaking changes get caught in CI, not production&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You set up automated checks that fail the build if someone tries to remove a required field or change a response type. No more "oops, we broke all the mobile clients" moments.&lt;/p&gt;


&lt;h2&gt;
  
  
  Quick Start: Your First OpenAPI Contract
&lt;/h2&gt;

&lt;p&gt;Let's build a simple orders API. We'll start with the contract.&lt;/p&gt;

&lt;p&gt;Create a file: &lt;code&gt;contracts/openapi/orders-api.yaml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;openapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.2.0&lt;/span&gt;
&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Orders API&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Contract-first REST API for order management&lt;/span&gt;

&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;/v1/orders&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create a new order&lt;/span&gt;
      &lt;span class="na"&gt;requestBody&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/CreateOrderRequest'&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;201'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Order created successfully&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/OrderResponse'&lt;/span&gt;
      &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;400'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Invalid request&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/ErrorResponse'&lt;/span&gt;

  &lt;span class="s"&gt;/v1/orders/{orderId}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get an order by ID&lt;/span&gt;
      &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;orderId&lt;/span&gt;
          &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;200'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Order found&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/OrderResponse'&lt;/span&gt;
      &lt;span class="err"&gt;  &lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;404'&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Order not found&lt;/span&gt;

&lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schemas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;CreateOrderRequest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;customerId&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;items&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;customerId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CUST-123"&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;array&lt;/span&gt;
          &lt;span class="na"&gt;minItems&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
          &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/OrderItem'&lt;/span&gt;

    &lt;span class="na"&gt;OrderItem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sku&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;quantity&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;sku&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SKU-001"&lt;/span&gt;
        &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;integer&lt;/span&gt;
          &lt;span class="na"&gt;minimum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
          &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;

    &lt;span class="na"&gt;OrderResponse&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;orderId&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;customerId&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;status&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;items&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;timestamp&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ORD-10001"&lt;/span&gt;
        &lt;span class="na"&gt;customerId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
        &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;CREATED&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;REJECTED&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;array&lt;/span&gt;
          &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/OrderItem'&lt;/span&gt;
        &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;date-time&lt;/span&gt;

    &lt;span class="na"&gt;ErrorResponse&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;code&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;message&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;timestamp&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;VALIDATION_ERROR"&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;items&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;must&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;not&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;be&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;empty"&lt;/span&gt;
        &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;date-time&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's your contract. It defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two endpoints: POST &lt;code&gt;/v1/orders&lt;/code&gt; and GET &lt;code&gt;/v1/orders/{orderId}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Request and response structures&lt;/li&gt;
&lt;li&gt;Error responses&lt;/li&gt;
&lt;li&gt;Validation rules (like &lt;code&gt;minItems: 1&lt;/code&gt; for the items array)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Implementing the API in Spring Boot
&lt;/h2&gt;

&lt;p&gt;Now let's build the actual API. Create a Spring Boot controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="nd"&gt;@RequestMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/v1/orders"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class="nd"&gt;@Slf4j&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt; &lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@PostMapping&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;OrderResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;createOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nd"&gt;@Valid&lt;/span&gt; &lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;CreateOrderRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Creating order for customer: {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;customerId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="nc"&gt;OrderResponse&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;orderService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CREATED&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/{orderId}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;OrderResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;OrderResponse&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;orderService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ResourceNotFoundException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order not found: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@Valid&lt;/code&gt; triggers validation based on the contract constraints&lt;/li&gt;
&lt;li&gt;HTTP status codes match what the contract specifies (201 for created, 404 for not found)&lt;/li&gt;
&lt;li&gt;DTOs (CreateOrderRequest, OrderResponse) match the contract schemas exactly&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Adding Contract Validation to CI
&lt;/h2&gt;

&lt;p&gt;Here's how to catch breaking changes before they reach production.&lt;/p&gt;

&lt;p&gt;Add this to your GitHub Actions workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Validate API Contract&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;pull_request&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contract-validation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check for breaking changes&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npx openapi-diff \&lt;/span&gt;
            &lt;span class="s"&gt;origin/main:contracts/openapi/orders-api.yaml \&lt;/span&gt;
            &lt;span class="s"&gt;HEAD:contracts/openapi/orders-api.yaml \&lt;/span&gt;
            &lt;span class="s"&gt;--fail-on-breaking&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now if someone tries to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove a required field&lt;/li&gt;
&lt;li&gt;Change a field type&lt;/li&gt;
&lt;li&gt;Remove an endpoint&lt;/li&gt;
&lt;li&gt;Change HTTP status codes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The PR build will fail. Breaking changes get caught in code review.&lt;/p&gt;




&lt;h2&gt;
  
  
  Contract-First for Kafka Events
&lt;/h2&gt;

&lt;p&gt;APIs are one thing, but what about event-driven systems? Let's look at Kafka.&lt;/p&gt;

&lt;p&gt;For Kafka, you need two contracts:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Topic Semantics (Human-Readable)
&lt;/h3&gt;

&lt;p&gt;Create: &lt;code&gt;contracts/events/topics.md&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## orders.order-created.v1&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Purpose: Emitted when an order is created
&lt;span class="p"&gt;-&lt;/span&gt; Key: orderId (for partition affinity)
&lt;span class="p"&gt;-&lt;/span&gt; Delivery: At-least-once (consumers must be idempotent)
&lt;span class="p"&gt;-&lt;/span&gt; Deduplication: Use eventId field
&lt;span class="p"&gt;-&lt;/span&gt; DLQ: orders.order-created.v1.dlq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This documents the operational behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Avro Schema (Machine-Validated)
&lt;/h3&gt;

&lt;p&gt;Create: &lt;code&gt;contracts/events/avro/OrderCreated.v1.avsc&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"record"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OrderCreated"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.acme.events"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eventId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"doc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Unique event ID for deduplication"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"occurredAt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"doc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISO 8601 timestamp"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"orderId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customerId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"record"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OrderItem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"int"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Publishing Kafka Events
&lt;/h2&gt;

&lt;p&gt;Here's how to publish events in Spring Boot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="nd"&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class="nd"&gt;@Slf4j&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderEventPublisher&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;KafkaTemplate&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;kafkaTemplate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;publishOrderCreated&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderCreated&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrderId&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Publishing OrderCreated: orderId={}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SendResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;future&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                &lt;span class="n"&gt;kafkaTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"orders.order-created.v1"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;whenComplete&lt;/span&gt;&lt;span class="o"&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;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Published OrderCreated: partition={}, offset={}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRecordMetadata&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;partition&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
                        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRecordMetadata&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to publish OrderCreated"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;});&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why use orderId as the key?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Kafka partitions messages by key. Using &lt;code&gt;orderId&lt;/code&gt; ensures all events for the same order go to the same partition, preserving order.&lt;/p&gt;




&lt;h2&gt;
  
  
  Consuming Kafka Events (The Right Way)
&lt;/h2&gt;

&lt;p&gt;Kafka delivers messages at-least-once, which means duplicates are possible. Your consumer must be idempotent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
&lt;span class="nd"&gt;@KafkaListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;topics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"orders.order-created.v1"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groupId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"billing-service"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onOrderCreated&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderCreated&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Check if we already processed this event&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;processedEventsRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;existsByEventId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEventId&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Skipping duplicate event: {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEventId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Process the event&lt;/span&gt;
    &lt;span class="n"&gt;billingService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createInvoice&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrderId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCustomerId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getItems&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Mark as processed&lt;/span&gt;
    &lt;span class="n"&gt;processedEventsRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ProcessedEvent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEventId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern prevents duplicate processing. Even if Kafka redelivers the same event, you check the &lt;code&gt;eventId&lt;/code&gt; and skip it if you've seen it before.&lt;/p&gt;




&lt;h2&gt;
  
  
  Schema Evolution Done Right
&lt;/h2&gt;

&lt;p&gt;Eventually you'll need to add fields to your events. Here's how to do it without breaking consumers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wrong way (breaks consumers):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes &lt;code&gt;source&lt;/code&gt; required. Old events don't have it. New consumers crash when reading old events.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Right way (backward compatible):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"null"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Old consumers reading new events: They ignore the &lt;code&gt;source&lt;/code&gt; field&lt;/li&gt;
&lt;li&gt;New consumers reading old events: They get &lt;code&gt;null&lt;/code&gt; for &lt;code&gt;source&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nobody breaks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always make new fields optional with defaults.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting Up Schema Registry
&lt;/h2&gt;

&lt;p&gt;Schema Registry validates that your schema changes are backward compatible.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;application.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;kafka&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;bootstrap-servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost:9092&lt;/span&gt;
    &lt;span class="na"&gt;producer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;key-serializer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;org.apache.kafka.common.serialization.StringSerializer&lt;/span&gt;
      &lt;span class="na"&gt;value-serializer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;io.confluent.kafka.serializers.KafkaAvroSerializer&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;schema.registry.url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://localhost:8081&lt;/span&gt;
        &lt;span class="na"&gt;auto.register.schemas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;use.latest.version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When your producer starts, it tries to register the schema. If the schema isn't backward compatible with the previous version, registration fails and your service won't start.&lt;/p&gt;

&lt;p&gt;This prevents breaking changes from reaching production.&lt;/p&gt;




&lt;h2&gt;
  
  
  Database Contracts With Flyway
&lt;/h2&gt;

&lt;p&gt;Database schemas are contracts too. Use Flyway to version your migrations.&lt;/p&gt;

&lt;p&gt;Create: &lt;code&gt;src/main/resources/db/migration/V1__create_orders.sql&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;          &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt;      &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt;  &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt;   &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;order_items&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;sku&lt;/span&gt;      &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;         &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_orders_customer_id&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flyway runs migrations in order on startup. If someone manually modified the schema, Flyway detects the mismatch and fails the deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Wins Checklist
&lt;/h2&gt;

&lt;p&gt;Here's what you can do this week:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Day 1: Write one OpenAPI contract&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pick your simplest API endpoint&lt;/li&gt;
&lt;li&gt;Write the OpenAPI spec for it&lt;/li&gt;
&lt;li&gt;Validate it with &lt;a href="https://editor.swagger.io" rel="noopener noreferrer"&gt;editor.swagger.io&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Day 2: Set up contract validation in CI&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add openapi-diff to your GitHub Actions&lt;/li&gt;
&lt;li&gt;Test it by trying to make a breaking change&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Day 3: Create a mock server&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Prism or Wiremock to serve responses based on your contract&lt;/li&gt;
&lt;li&gt;Give the mock URL to your frontend team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Day 4: Implement the real API&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build the Spring Boot controller that matches the contract&lt;/li&gt;
&lt;li&gt;Run your contract validation to ensure it matches&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Day 5: Measure the difference&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Count integration bugs before and after&lt;/li&gt;
&lt;li&gt;Measure time to integration&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Mistake 1: Generating contracts from code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don't use annotations to generate OpenAPI specs. Write the spec first, then implement code that matches it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 2: Skipping CI validation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Contracts are useless if they're not enforced. Always add automated checks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 3: Breaking changes without versioning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you need to make a breaking change, create a new version (&lt;code&gt;/v2/orders&lt;/code&gt;) and deprecate the old one gradually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 4: Forgetting about idempotency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Always include an &lt;code&gt;idempotencyKey&lt;/code&gt; field for POST requests and an &lt;code&gt;eventId&lt;/code&gt; for Kafka events. Retries happen. Your code must handle them gracefully.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tools That Help
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;OpenAPI:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://editor.swagger.io" rel="noopener noreferrer"&gt;Swagger Editor&lt;/a&gt; - Validate your specs&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://stoplight.io/open-source/prism" rel="noopener noreferrer"&gt;Prism&lt;/a&gt; - Mock server from OpenAPI&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openapi-generator.tech" rel="noopener noreferrer"&gt;openapi-generator&lt;/a&gt; - Generate client SDKs&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/openapi-diff" rel="noopener noreferrer"&gt;openapi-diff&lt;/a&gt; - Detect breaking changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Kafka:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.confluent.io/platform/current/schema-registry/index.html" rel="noopener noreferrer"&gt;Confluent Schema Registry&lt;/a&gt; - Schema validation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://avro.apache.org/docs/current/gettingstartedjava.html" rel="noopener noreferrer"&gt;Avro Tools&lt;/a&gt; - Generate Java classes from schemas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Database:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://flywaydb.org" rel="noopener noreferrer"&gt;Flyway&lt;/a&gt; - Database migrations&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.liquibase.org" rel="noopener noreferrer"&gt;Liquibase&lt;/a&gt; - Alternative to Flyway&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What's Your Experience?
&lt;/h2&gt;

&lt;p&gt;Have you tried contract-first integration? What worked? What didn't?&lt;/p&gt;

&lt;p&gt;Drop a comment below. I'd love to hear about your experiences and answer any questions.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Want the full code?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check out the complete working example: &lt;a href="https://github.com/wallaceespindola/contract-first-integrations" rel="noopener noreferrer"&gt;github.com/wallaceespindola/contract-first-integrations&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full OpenAPI specs&lt;/li&gt;
&lt;li&gt;Spring Boot implementation&lt;/li&gt;
&lt;li&gt;Kafka producer and consumer with Avro&lt;/li&gt;
&lt;li&gt;Flyway migrations&lt;/li&gt;
&lt;li&gt;CI/CD validation setup&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If this helped you, hit the ❤️ button and share it with your team. Follow me for more practical Spring Boot and Kafka tutorials.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Related posts you might like:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/wallaceespindola/generating-realistic-fake-data-in-java-with-quarkus-datafaker-easyrandom-5gi8"&gt;Generating Realistic Fake Data in Java with Quarkus, DataFaker &amp;amp; EasyRandom&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/wallaceespindola/cassandra-vs-postgresql-a-developers-guide-to-choose-the-right-database-3nhi"&gt;Cassandra vs PostgreSQL: A Developer’s Guide to Choose the Right Database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/wallaceespindola/fastapi-your-fast-and-modern-framework-for-apis-3mmo"&gt;FastAPI Unleashed: Building Modern and High-Performance APIs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Need more tech insights?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check out my &lt;a href="https://github.com/wallaceespindola" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/wallaceespindola/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, and &lt;a href="https://speakerdeck.com/wallacese" rel="noopener noreferrer"&gt;Speaker Deck&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Support my work: &lt;a href="//buymeacoffee.com/wallace.espindola"&gt;Buy Me A Coffee&lt;/a&gt; ☕&lt;/p&gt;

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

</description>
      <category>java</category>
      <category>microservices</category>
      <category>architecture</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Generating Realistic Fake Data in Java with Quarkus, DataFaker &amp; EasyRandom</title>
      <dc:creator>Wallace Espindola</dc:creator>
      <pubDate>Tue, 07 Oct 2025 17:47:43 +0000</pubDate>
      <link>https://dev.to/wallaceespindola/generating-realistic-fake-data-in-java-with-quarkus-datafaker-easyrandom-5gi8</link>
      <guid>https://dev.to/wallaceespindola/generating-realistic-fake-data-in-java-with-quarkus-datafaker-easyrandom-5gi8</guid>
      <description>&lt;p&gt;Let’s be honest — your test data probably looks a little… robotic.&lt;br&gt;
You know the drill: “John Doe”, “&lt;a href="mailto:test@example.com"&gt;test@example.com&lt;/a&gt;”, “123 Main Street”.&lt;/p&gt;

&lt;p&gt;It works, but it’s painfully obvious that it’s fake.&lt;/p&gt;

&lt;p&gt;And if you’re demoing your API, seeding a database, or stress-testing your app, that kind of data just doesn’t cut it anymore.&lt;/p&gt;

&lt;p&gt;The good news? With &lt;strong&gt;Quarkus&lt;/strong&gt;, &lt;strong&gt;DataFaker&lt;/strong&gt;, and &lt;strong&gt;EasyRandom&lt;/strong&gt;, you can generate realistic, varied, and structured data — fast, elegant, and believable.&lt;/p&gt;

&lt;p&gt;By the end of this article, you’ll have a &lt;strong&gt;Quarkus REST API&lt;/strong&gt; that returns lifelike fake users — names, emails, phones, and addresses — all neatly timestamped.&lt;/p&gt;

&lt;p&gt;Full code example here: &lt;br&gt;
👉 &lt;a href="https://github.com/wallaceespindola/fake-data-quarkus" rel="noopener noreferrer"&gt;github.com/wallaceespindola/fake-data-quarkus&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s dive in.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why Fake Data Matters More Than Ever
&lt;/h2&gt;

&lt;p&gt;If you’re a backend dev, you’ve probably needed mock data for one of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Testing APIs or UI components&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Populating demo environments&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Running performance or load tests&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Anonymizing real data for staging environments&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And sure, you could throw in “John Doe” again.&lt;br&gt;
But what if you could generate realistic, localized names, addresses, and emails that feel like real-world data — and do it dynamically, in a fast Quarkus service?&lt;/p&gt;

&lt;p&gt;That’s what we’re building.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;p&gt;We’ll use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quarkus&lt;/strong&gt; — the fast, lightweight Java framework for modern apps&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DataFaker&lt;/strong&gt; — for generating realistic values&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;EasyRandom&lt;/strong&gt; — for filling entire objects automatically&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lombok&lt;/strong&gt; — to cut boilerplate&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OpenAPI&lt;/strong&gt; + Swagger UI — for quick API testing&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;Generate a Quarkus project using the CLI or code.quarkus.io:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn io.quarkus.platform:quarkus-maven-plugin:3.12.1:create &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-DprojectGroupId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;com.example &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-DprojectArtifactId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fake-data-quarkus &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-DclassName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"com.example.fakedata.UserResource"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-Dpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/api/users"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, add dependencies to your pom.xml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.quarkus&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;quarkus-resteasy-reactive&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.quarkus&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;quarkus-smallrye-openapi&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;net.datafaker&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;datafaker&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.2.2&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.jeasy&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;easy-random-core&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.0.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.projectlombok&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;lombok&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;optional&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/optional&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.quarkus&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;quarkus-junit5&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Domain
&lt;/h2&gt;

&lt;p&gt;Let’s start small — our fake “User” object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.fakedata.domain&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Builder&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Data&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Data&lt;/span&gt;
&lt;span class="nd"&gt;@Builder&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;fullName&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a DTO version for API responses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.fakedata.dto&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;UserDto&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;fullName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re using &lt;strong&gt;Lombok&lt;/strong&gt; for the model and a &lt;strong&gt;Java Record&lt;/strong&gt; for the &lt;strong&gt;DTO&lt;/strong&gt; to keep things clean.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Data Generation Service
&lt;/h2&gt;

&lt;p&gt;Here’s where we mix &lt;strong&gt;DataFaker&lt;/strong&gt; and &lt;strong&gt;EasyRandom&lt;/strong&gt; for maximum awesomeness:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.fakedata.service&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.example.fakedata.domain.User&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.enterprise.context.ApplicationScoped&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;net.datafaker.Faker&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.jeasy.random.EasyRandom&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.jeasy.random.EasyRandomParameters&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.List&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Locale&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.UUID&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.stream.Collectors&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.stream.IntStream&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@ApplicationScoped&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DataGenService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Faker&lt;/span&gt; &lt;span class="n"&gt;faker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Faker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Locale&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ENGLISH&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;EasyRandom&lt;/span&gt; &lt;span class="n"&gt;easyRandom&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;DataGenService&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;EasyRandomParameters&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EasyRandomParameters&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stringLengthRange&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;easyRandom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EasyRandom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="nf"&gt;randomUserViaDatafaker&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;randomUUID&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fullName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;fullName&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;internet&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;emailAddress&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;phone&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;phoneNumber&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;cellPhone&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;fullAddress&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="nf"&gt;randomUserViaEasyRandom&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;easyRandom&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;nextObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isBlank&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
      &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;randomUUID&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setFullName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;fullName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setEmail&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;faker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;internet&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;emailAddress&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;manyUsers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;useEasyRandom&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;IntStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mapToObj&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;useEasyRandom&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;randomUserViaEasyRandom&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;randomUserViaDatafaker&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s the beauty of it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DataFaker&lt;/strong&gt; generates names, addresses, and emails that sound real.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;EasyRandom&lt;/strong&gt; handles the object creation for you.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Combine them, and you’ve got structured data that looks human.&lt;/p&gt;




&lt;h2&gt;
  
  
  The REST API
&lt;/h2&gt;

&lt;p&gt;Now, let’s make it accessible with &lt;strong&gt;Quarkus RESTEasy Reactive&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.fakedata&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.example.fakedata.domain.User&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.example.fakedata.dto.UserDto&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.example.fakedata.service.DataGenService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.inject.Inject&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.*&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.core.MediaType&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;jakarta.ws.rs.core.Response&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.eclipse.microprofile.openapi.annotations.Operation&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.eclipse.microprofile.openapi.annotations.tags.Tag&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.time.Instant&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.List&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.stream.Collectors&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/users"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Produces&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Tag&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Users"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Generate fake users with DataFaker + EasyRandom"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserResource&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="nd"&gt;@Inject&lt;/span&gt;
  &lt;span class="nc"&gt;DataGenService&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nc"&gt;ApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;ApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;T&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@GET&lt;/span&gt;
  &lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/{count}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nd"&gt;@Operation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Generate a list of fake users"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt; &lt;span class="nf"&gt;generateUsers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"count"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                &lt;span class="nd"&gt;@QueryParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"easy"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nd"&gt;@DefaultValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;easy&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserDto&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;manyUsers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;easy&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserDto&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getFullName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmail&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPhone&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAddress&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ApiResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@GET&lt;/span&gt;
  &lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/one"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nd"&gt;@Operation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Generate one fake user"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt; &lt;span class="nf"&gt;oneUser&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;randomUserViaDatafaker&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ApiResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserDto&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getFullName&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmail&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPhone&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAddress&lt;/span&gt;&lt;span class="o"&gt;()))).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  This API gives you two simple endpoints:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;GET /api/users/{count}&lt;/code&gt; — returns N users&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;GET /api/users/one&lt;/code&gt; — returns a single fake user&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every response includes a timestamp, so you know exactly when it was generated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run It
&lt;/h3&gt;

&lt;p&gt;Start Quarkus in dev mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./mvnw quarkus:dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Visit Home Test Page:
&lt;/h3&gt;

&lt;p&gt;👉 &lt;a href="http://localhost:8080/" rel="noopener noreferrer"&gt;http://localhost:8080/&lt;/a&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%2Fgtzt5jewabtlf03n4ll0.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%2Fgtzt5jewabtlf03n4ll0.png" alt=" " width="800" height="676"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Visit Swagger Page for Endpoints:
&lt;/h3&gt;

&lt;p&gt;👉 &lt;a href="http://localhost:8080/q/swagger-ui" rel="noopener noreferrer"&gt;http://localhost:8080/q/swagger-ui&lt;/a&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%2Fhqgymgffoxadi2ou74zm.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%2Fhqgymgffoxadi2ou74zm.png" alt=" " width="800" height="732"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GET /api/users/3&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a8a5fbb3-47b6-4387-9827-8c9b8ac6e9dc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fullName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rosa Bianchi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rosa.bianchi@example.it"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"+39 349 123 4567"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Via Roma 45, Milano"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"c48de9e7-92f4-4b15-8bcb-3c1b3a710c0c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"fullName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"James Holloway"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"james.holloway@demo.io"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"phone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"+1 415 555 1829"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"77 Market Street, San Francisco"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-10-06T18:45:33.982Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks real enough to demo your next project, right?&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus: Built-In Swagger &amp;amp; OpenAPI
&lt;/h2&gt;

&lt;p&gt;Quarkus comes with OpenAPI + Swagger UI ready to go.&lt;/p&gt;

&lt;p&gt;No config hell, no dependencies to chase. Just hit &lt;code&gt;/q/swagger-ui&lt;/code&gt; and explore your endpoints visually.&lt;/p&gt;

&lt;h3&gt;
  
  
  Want to tweak the OpenAPI path?
&lt;/h3&gt;

&lt;p&gt;Just update application.properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;quarkus.smallrye-openapi.path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/openapi&lt;/span&gt;
&lt;span class="py"&gt;quarkus.swagger-ui.always-include&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why This Combo Works
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DataFaker&lt;/strong&gt; brings authenticity — localized names, realistic emails, believable data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;EasyRandom&lt;/strong&gt; handles complexity — fills entire objects without effort.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quarkus&lt;/strong&gt; ties it together with speed and simplicity — minimal setup, instant hot reloads, and small footprint.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You end up with a lightning-fast, production-like data generator that’s perfect for modern development.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Preloading a test database&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Filling front-end prototypes with real-looking data&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stress-testing APIs with variety&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Creating synthetic datasets for analytics&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Replacing sensitive info in QA or sandbox environments&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Going Native (Optional)
&lt;/h2&gt;

&lt;p&gt;Want to go wild? Build it as a native binary using GraalVM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./mvnw package &lt;span class="nt"&gt;-Pnative&lt;/span&gt;
./target/fake-data-quarkus-1.0.0-runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Startup time? Instant.&lt;br&gt;
Memory footprint? Tiny.&lt;br&gt;
Speed? Ridiculous.&lt;/p&gt;

&lt;p&gt;Perfect for containers, CI pipelines, or quick mock environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Fake data doesn’t have to be boring.&lt;br&gt;
With Quarkus, DataFaker, and EasyRandom, you can generate realistic, rich, and structured data that makes your applications — and demos — feel alive.&lt;/p&gt;

&lt;p&gt;This setup is fast, modern, and ready to use for everything from testing to production seeding.&lt;/p&gt;

&lt;p&gt;So next time you’re building or testing an API, forget “John Doe”.&lt;br&gt;
Give your fake data a little soul.&lt;/p&gt;




&lt;h2&gt;
  
  
  Need more tech insights?
&lt;/h2&gt;

&lt;p&gt;Check out my &lt;a href="https://github.com/wallaceespindola" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://www.linkedin.com/in/wallaceespindola" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;, and &lt;a href="https://speakerdeck.com/wallacese" rel="noopener noreferrer"&gt;SpeakerDeck&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Support my work: &lt;a href="https://buymeacoffee.com/wallace.espindola" rel="noopener noreferrer"&gt;Buy Me A Coffee&lt;/a&gt; ☕&lt;/p&gt;

</description>
      <category>testing</category>
      <category>java</category>
      <category>quarkus</category>
      <category>restapi</category>
    </item>
    <item>
      <title>Cassandra vs PostgreSQL: A Developer’s Guide to Choose the Right Database</title>
      <dc:creator>Wallace Espindola</dc:creator>
      <pubDate>Wed, 09 Jul 2025 15:48:42 +0000</pubDate>
      <link>https://dev.to/wallaceespindola/cassandra-vs-postgresql-a-developers-guide-to-choose-the-right-database-3nhi</link>
      <guid>https://dev.to/wallaceespindola/cassandra-vs-postgresql-a-developers-guide-to-choose-the-right-database-3nhi</guid>
      <description>&lt;p&gt;Choosing the right database can feel a bit like picking the right tool for a job—you wouldn't use a hammer to tighten a screw, right? In the world of databases, two heavy-weight options often come up: &lt;strong&gt;Apache Cassandra&lt;/strong&gt; and &lt;strong&gt;PostgreSQL&lt;/strong&gt;. Both are powerful, but they shine in different scenarios. Let's dive into their strengths, weaknesses, and ideal use cases to help you make an informed decision.&lt;/p&gt;




&lt;h1&gt;
  
  
  Understanding the Basics
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Apache Cassandra
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cassandra&lt;/strong&gt; is a distributed NoSQL database designed for handling large amounts of data across many servers. It's known for its high availability and scalability. Companies like Apple and Netflix rely on Cassandra to manage massive datasets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Apple&lt;/strong&gt;: Reportedly runs over 75,000 Cassandra nodes, storing more than 10 petabytes of data. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Netflix&lt;/strong&gt;: Uses Cassandra to handle its ever-growing persistence needs. &lt;/p&gt;

&lt;h2&gt;
  
  
  PostgreSQL
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt; is a powerful, open-source relational database system known for its robustness and standards compliance. It's widely used in various applications, from web development to data analysis. GitLab, for instance, uses PostgreSQL as its primary database system. &lt;/p&gt;




&lt;h2&gt;
  
  
  Key Differences
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Cassandra&lt;/th&gt;
&lt;th&gt;PostgreSQL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Wide-column store (NoSQL)&lt;/td&gt;
&lt;td&gt;Relational (SQL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Horizontally scalable across many servers&lt;/td&gt;
&lt;td&gt;Vertically scalable; horizontal scaling possible with extensions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consistency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Eventual consistency (tunable)&lt;/td&gt;
&lt;td&gt;Strong consistency (ACID compliant)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Query Language&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CQL (Cassandra Query Language)&lt;/td&gt;
&lt;td&gt;SQL (Structured Query Language)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Use Cases&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High write throughput, IoT, real-time analytics&lt;/td&gt;
&lt;td&gt;Complex queries, transactional systems, analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Community Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Active community with enterprise backing&lt;/td&gt;
&lt;td&gt;Large, active open-source community&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  When to Choose Cassandra
&lt;/h2&gt;

&lt;p&gt;Cassandra excels in scenarios where you need to handle large volumes of data with high availability and scalability. Consider Cassandra if:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;High Write Throughput&lt;/strong&gt;: Your application requires writing large amounts of data quickly, such as logging or sensor data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distributed Architecture&lt;/strong&gt;: You need a database that can run across multiple data centers or cloud regions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fault Tolerance&lt;/strong&gt;: Your system must remain operational even if parts of it fail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: A global e-commerce platform tracking user activity in real-time across various regions.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to Choose PostgreSQL
&lt;/h2&gt;

&lt;p&gt;PostgreSQL is ideal when your application requires complex queries, transactions, and data integrity. Opt for PostgreSQL if:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complex Queries&lt;/strong&gt;: You need to perform joins, aggregations, and subqueries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data Integrity&lt;/strong&gt;: Your application requires strict adherence to ACID properties.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extensibility&lt;/strong&gt;: You plan to use extensions like PostGIS for geospatial data or TimescaleDB for time-series data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: A financial application managing transactions and generating detailed reports.&lt;/p&gt;




&lt;h1&gt;
  
  
  Real-World Use Cases
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Cassandra at Apple
&lt;/h2&gt;

&lt;p&gt;Apple uses Cassandra to manage services like iMessage and iTunes, running over 75,000 nodes and storing more than 10 petabytes of data. &lt;/p&gt;

&lt;h2&gt;
  
  
  PostgreSQL at GitLab
&lt;/h2&gt;

&lt;p&gt;GitLab relies on PostgreSQL for its database needs, emphasizing its robustness and reliability. &lt;/p&gt;




&lt;h1&gt;
  
  
  Performance and Scalability
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Cassandra
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: Designed for horizontal scaling; adding more nodes increases capacity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Optimized for write-heavy workloads; reads can be fast with proper data modeling.&lt;/p&gt;

&lt;h2&gt;
  
  
  PostgreSQL
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;: Primarily scales vertically; horizontal scaling achievable with tools like Citus.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Excels in read-heavy workloads and complex queries; write performance is strong but may require tuning for very high volumes.&lt;/p&gt;




&lt;h1&gt;
  
  
  Community and Ecosystem
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Cassandra
&lt;/h2&gt;

&lt;p&gt;Backed by the Apache Software Foundation, Cassandra has a robust ecosystem with tools for monitoring, management, and integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  PostgreSQL
&lt;/h2&gt;

&lt;p&gt;PostgreSQL boasts a vast array of extensions and tools, such as PostGIS for geospatial data and TimescaleDB for time-series data. Its active community ensures continuous improvement and support.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Choosing between Cassandra and PostgreSQL depends on your specific needs:&lt;/p&gt;

&lt;p&gt;Opt for Cassandra if you require a highly scalable, fault-tolerant system capable of handling massive write loads across distributed environments.&lt;/p&gt;

&lt;p&gt;Choose PostgreSQL when your application demands complex queries, strong data integrity, and a rich set of features out of the box.&lt;/p&gt;

&lt;p&gt;Both databases are powerful in their domains. Understanding your application's requirements will guide you to the right choice.&lt;/p&gt;




&lt;h1&gt;
  
  
  References
&lt;/h1&gt;

&lt;p&gt;Cassandra's documentation: &lt;a href="https://cassandra.apache.org/" rel="noopener noreferrer"&gt;https://cassandra.apache.org/&lt;/a&gt;&lt;br&gt;
PostgreSQL's documentation: &lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;https://www.postgresql.org/&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Need more tech insights?
&lt;/h2&gt;

&lt;p&gt;Check out my &lt;a href="https://github.com/wallaceespindola" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; repo and my &lt;a href="https://www.linkedin.com/in/wallaceespindola" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; page. &lt;br&gt;
Some of my presentation slides are available &lt;a href="https://speakerdeck.com/wallacese" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
Do you want to buy me a coffee to elevate my energy? You can do it &lt;a href="https://buymeacoffee.com/wallace.espindola" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

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

</description>
      <category>database</category>
      <category>architecture</category>
      <category>performance</category>
      <category>opensource</category>
    </item>
    <item>
      <title>FastAPI Unleashed: Building Modern and High-Performance APIs</title>
      <dc:creator>Wallace Espindola</dc:creator>
      <pubDate>Tue, 20 May 2025 17:41:01 +0000</pubDate>
      <link>https://dev.to/wallaceespindola/fastapi-your-fast-and-modern-framework-for-apis-3mmo</link>
      <guid>https://dev.to/wallaceespindola/fastapi-your-fast-and-modern-framework-for-apis-3mmo</guid>
      <description>&lt;p&gt;If you’re coding in Python and need to whip up a blazing-fast API with minimal fuss, &lt;strong&gt;FastAPI&lt;/strong&gt; is the framework you’ve been waiting for. It brings together ease of use, powerful data validation, and auto-generated API docs into one neat package. Below, we’ll walk through why FastAPI rocks and use code snippets to highlight its best features.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;FastAPI&lt;/strong&gt; is a &lt;strong&gt;Python&lt;/strong&gt; web framework designed for building APIs quickly and efficiently. Leveraging Python’s async and await keywords, it takes advantage of &lt;strong&gt;non-blocking I/O&lt;/strong&gt; to handle high loads. Under the hood, it uses &lt;strong&gt;Starlette&lt;/strong&gt; for HTTP and &lt;strong&gt;Pydantic&lt;/strong&gt; for data validation, making the development process clean and robust.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Highlights
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;High-performance:&lt;/strong&gt; Comparable to Node.js and Go in many benchmarks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type Hints:&lt;/strong&gt; Strongly tied to type hints for validating and documenting your APIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interactive Docs:&lt;/strong&gt; Automatic generation of docs using &lt;strong&gt;Swagger UI&lt;/strong&gt; and &lt;strong&gt;ReDoc&lt;/strong&gt; right out of the box.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Basic API: Hello, FastAPI!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s start simple, so here’s a basic hello-world to start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# A simple GET endpoint
&lt;/span&gt;&lt;span class="nd"&gt;@app.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;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_root&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&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;Hello, FastAPI!&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 this example, you can see how FastAPI simple to implement and very compact.&lt;/p&gt;




&lt;h2&gt;
  
  
  Now a simple API with GET and POST Endpoints
&lt;/h2&gt;

&lt;p&gt;Here’s a basic example with GET/POST. Let's suppose you want to build an API to store item data in a hypothetical e-commerce application. With FastAPI, you can define routes in just a few lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;
    &lt;span class="n"&gt;tax&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

&lt;span class="nd"&gt;@app.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;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_root&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&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;Hello from root path!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@app.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;/items/{item_id}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item_id&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;q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@app.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;/items/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_item&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="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;item&lt;/span&gt;&lt;span class="sh"&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="nf"&gt;dict&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;What’s Going On?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;app = FastAPI()&lt;/code&gt;: Instantiates your FastAPI application.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Item class&lt;/code&gt;: Uses Pydantic to define the data model. The &lt;code&gt;BaseModel&lt;/code&gt; approach means any JSON coming into the endpoint will be validated automatically.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@app.get("/")&lt;/code&gt;: A simple GET endpoint that returns a JSON greeting.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@app.get("/items/{item_id}")&lt;/code&gt;: Parameterizing the endpoint to retrieve a specific item (by &lt;code&gt;item_id&lt;/code&gt;) and an optional query parameter &lt;code&gt;q&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@app.post("/items/")&lt;/code&gt;: A POST endpoint to create a new item. Because it’s defined with &lt;code&gt;item: Item&lt;/code&gt;, FastAPI will expect a JSON body matching the &lt;code&gt;Item&lt;/code&gt; schema.&lt;/p&gt;




&lt;h2&gt;
  
  
  Making Life Easier with Dependency Injection
&lt;/h2&gt;

&lt;p&gt;Now, imagine you need the same parameter—like a search query—across multiple endpoints. Instead of duplicating logic, you can lean on FastAPI’s built-in dependency injection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Depends&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;common_parameters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@app.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;/items/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commons&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;common_parameters&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;commons&lt;/span&gt;

&lt;span class="nd"&gt;@app.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;/users/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;read_users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commons&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Depends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;common_parameters&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users_query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;commons&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;Why Dependency Injection?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reusability:&lt;/strong&gt; Define a common function once and share it with multiple routes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maintainability:&lt;/strong&gt; Centralize logic (like parsing query parameters or handling authentication) in one function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clean Code:&lt;/strong&gt; Keep your endpoint functions minimal and focused on core logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Background Tasks:&lt;/strong&gt; Handling Work Behind the Scenes&lt;br&gt;
Sometimes you want to do something without making the user wait—like sending a notification or writing to a log. That’s where background tasks come in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BackgroundTasks&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;log.txt&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;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.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;/send-notification/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;background_tasks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BackgroundTasks&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;background_tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;write_log&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Notification sent!&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="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&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;Notification will be sent in the background&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;
  
  
  How Does It Work?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Background Tasks:&lt;/strong&gt; A special FastAPI parameter that queues up tasks to run after your endpoint returns a response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Immediate Responses:&lt;/strong&gt; Your user gets a success message right away, while the server quietly handles the heavy lifting or logging behind the scenes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Should You Use FastAPI?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Developer Productivity:&lt;/strong&gt;&lt;br&gt;
The intuitive design and interactive docs mean you spend less time on boilerplate and more time on what matters—your business logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance &amp;amp; Scalability:&lt;/strong&gt;&lt;br&gt;
By using async/await, FastAPI manages high throughput gracefully, making it a strong choice for microservices or real-time data apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automatic Documentation:&lt;/strong&gt;&lt;br&gt;
Just hit /docs in your browser, and you’ll see a Swagger UI ready to go. Testing endpoints is a piece of cake.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data Validation &amp;amp; Serialization:&lt;/strong&gt;&lt;br&gt;
Pydantic models (like our Item example) ensure the data coming in (and going out) matches your defined schema. It catches errors early, so you’re not left debugging invalid payloads later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Easy Integrations:&lt;/strong&gt;&lt;br&gt;
Want to integrate a database like PostgreSQL, or a Machine Learning model built in scikit-learn? FastAPI’s modular design and dependency injection make it super straightforward.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;FastAPI&lt;/strong&gt; is a breath of fresh air in the Python ecosystem for building APIs. Its combination of modern Python features, high performance, and user-friendly documentation sets it apart from other frameworks. Whether you’re a data scientist deploying a predictive model or a developer writing microservices, FastAPI is definitely worth a look.&lt;/p&gt;

&lt;p&gt;Ready to give it a spin? Install FastAPI with pip install fastapi uvicorn, throw in the examples above, and open your browser at &lt;a href="http://127.0.0.1:8000/docs" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/docs&lt;/a&gt; to see your new API in action.&lt;/p&gt;

&lt;p&gt;You can find all the code in this article in this &lt;a href="https://github.com/wallaceespindola/python-fastapi-demo" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;. &lt;/p&gt;




&lt;h2&gt;
  
  
  References, of course
&lt;/h2&gt;

&lt;p&gt;This project uses &lt;strong&gt;FastAPI&lt;/strong&gt;, &lt;strong&gt;Starlette&lt;/strong&gt;, &lt;strong&gt;Pydantic&lt;/strong&gt;, &lt;strong&gt;Swagger&lt;/strong&gt; and &lt;strong&gt;Redoc&lt;/strong&gt; in it. For detailed documentation, take a look at:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://fastapi.tiangolo.com/" rel="noopener noreferrer"&gt;FastAPI docs&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.starlette.io/" rel="noopener noreferrer"&gt;Starlette docs&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.pydantic.dev/latest/" rel="noopener noreferrer"&gt;Pydantic docs&lt;/a&gt;&lt;br&gt;
&lt;a href="https://swagger.io/" rel="noopener noreferrer"&gt;Swagger docs&lt;/a&gt;&lt;br&gt;
&lt;a href="https://redocly.com/docs/redoc" rel="noopener noreferrer"&gt;Redoc docs&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Need more tech insights?
&lt;/h2&gt;

&lt;p&gt;Check out my &lt;a href="https://github.com/wallaceespindola" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt; and my &lt;a href="https://www.linkedin.com/in/wallaceespindola" rel="noopener noreferrer"&gt;LinkedIn page&lt;/a&gt;. Some slides &lt;a href="https://speakerdeck.com/wallacese/python-fastapi-fast-and-modern-api-development" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
Do you want to buy me a coffee to elevate my energy? You can do it &lt;a href="https://buymeacoffee.com/wallace.espindola" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

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

</description>
      <category>fastapi</category>
      <category>restapi</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>NoSQL Fighters Arena: The Battle of Data Titans</title>
      <dc:creator>Wallace Espindola</dc:creator>
      <pubDate>Thu, 24 Apr 2025 22:38:38 +0000</pubDate>
      <link>https://dev.to/wallaceespindola/nosql-fighters-arena-the-battle-of-data-titans-22o7</link>
      <guid>https://dev.to/wallaceespindola/nosql-fighters-arena-the-battle-of-data-titans-22o7</guid>
      <description>&lt;p&gt;Welcome to the NoSQL Arena, where mighty heroes battle not with fists, but with queries, scalability, and performance. Each warrior brings their own superpowers, tactics, and secret weaknesses. Let's meet our combatants!&lt;/p&gt;




&lt;h2&gt;
  
  
  🌀 &lt;strong&gt;MongoDB — The Shapeshifter&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Codename:&lt;/strong&gt; The Document Bender&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Document&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class:&lt;/strong&gt; Agile Technomancer&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Superpowers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Master of flexible schemas — adapts to any data form&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fluent in JSON-like spellcasting (BSON)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Summons indexes for fast lookup in semi-structured chaos&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Needs configuration for true consistency&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Can get sluggish under massive joins&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best Battlegrounds:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CMS realms, e-commerce jungles, and fast-changing development fields.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚔️ &lt;strong&gt;Cassandra — The Warlord of Writes&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Codename:&lt;/strong&gt; The Column Crusher&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Wide-column&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class:&lt;/strong&gt; Distributed Commander&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Superpowers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Unmatched in handling high write volume&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stretches across continents with ease (multi-region champion)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Resilient under pressure — no single point of failure&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Requires deep strategic planning (data modeling isn't beginner-friendly)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Limited in query flexibility&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best Battlegrounds:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time analytics warzones, IoT strongholds, social networks under siege.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚡ &lt;strong&gt;Redis — The Speedster&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Codename:&lt;/strong&gt; The Memory Phantom&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Key-Value&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class:&lt;/strong&gt; Temporal Trickster&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Superpowers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Blinding in-memory speed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Time-bending queues, pub/sub, and leaderboards&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Can freeze and persist memory snapshots&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Short-term memory — must be carefully managed for durability&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not ideal for complex data battles&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best Battlegrounds:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Caching towers, session dungeons, messaging arenas.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🕸️ &lt;strong&gt;Neo4j — The Webweaver&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Codename:&lt;/strong&gt; The Graph Seer&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Graph&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class:&lt;/strong&gt; Relational Mystic&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Superpowers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Sees hidden connections — perfect for deep relationship logic&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Controls the Cypher of the Graph Realm&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Swift pathfinding through data networks&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Doesn’t scale horizontally like the others&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not built for heavy write storms&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best Battlegrounds:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fraud rings, social kingdoms, and recommendation labyrinths.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔄 &lt;strong&gt;Couchbase — The Syncmaster&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Codename:&lt;/strong&gt; The Hybrid Hacker&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Document + Key-Value&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class:&lt;/strong&gt; Cloud Rogue&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Superpowers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Fuses document power with key-value speed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Offline-first magic for mobile warriors&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Speaks SQL-like N1QL incantations&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Complex gear needed for full potential&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;High cost if not optimized&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best Battlegrounds:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time mobile raids, distributed cloud ops, and sync-centric conflicts.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ☁️ &lt;strong&gt;DynamoDB — The Cloudborn&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Codename:&lt;/strong&gt; The Serverless Sentinel&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Document + Key-Value&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class:&lt;/strong&gt; Scalable Guardian&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Superpowers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Instantly scales to cosmic proportions&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configurable consistency (strong or eventual)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Immune to infrastructure failure&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Costs can skyrocket in long campaigns&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Limited in query magic — needs indexes and design forethought&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best Battlegrounds:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serverless realms, battle carts, gaming arenas.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 &lt;strong&gt;Elasticsearch — The Searchblade&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Codename:&lt;/strong&gt; The Index Warden&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Search / Document&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class:&lt;/strong&gt; Text Archer&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Superpowers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Precision full-text strikes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Instant retrieval across massive scrolls&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data dashboard conjuring with Kibana&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Not a transactional fighter&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Can become a memory hog&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best Battlegrounds:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search quests, log-dungeons, observability citadels.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🗿 &lt;strong&gt;HBase — The Ancient Colossus&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Codename:&lt;/strong&gt; The Big Data Behemoth&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; Wide-column&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class:&lt;/strong&gt; Batch Titan&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Superpowers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Handles ancient, massive archives with ease&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Integrates with Hadoop scrolls and Spark fire&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Excels at sequential time-series combat&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Slow to move, hard to master&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Requires lots of provisioning and allies&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best Battlegrounds:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Telecom vaults, financial archives, historical timelines.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;NoSQL Fighters Arena — Comparison Summary&lt;/strong&gt;
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;DB Hero&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Spell Type&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Superpower&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Best Battlegrounds&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🌀 &lt;strong&gt;MongoDB — The Shapeshifter&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Document&lt;/td&gt;
&lt;td&gt;Schema flexibility, fast querying&lt;/td&gt;
&lt;td&gt;Web apps, CMS, e-commerce&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚔️ &lt;strong&gt;Cassandra — The Warlord of Writes&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Wide-column&lt;/td&gt;
&lt;td&gt;High write throughput, distributed scalability&lt;/td&gt;
&lt;td&gt;Real-time analytics, IoT, social networks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;⚡ &lt;strong&gt;Redis — The Speedster&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Key-Value&lt;/td&gt;
&lt;td&gt;In-memory speed, pub/sub magic&lt;/td&gt;
&lt;td&gt;Caching, messaging systems, leaderboards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🕸️ &lt;strong&gt;Neo4j — The Webweaver&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Graph&lt;/td&gt;
&lt;td&gt;Powerful relationship navigation (Cypher)&lt;/td&gt;
&lt;td&gt;Social graphs, recommendations, fraud detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🔄 &lt;strong&gt;Couchbase — The Syncmaster&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Document + Key-Value&lt;/td&gt;
&lt;td&gt;Mobile sync, hybrid model flexibility&lt;/td&gt;
&lt;td&gt;Offline-first apps, real-time APIs, caching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;☁️ &lt;strong&gt;DynamoDB — The Cloudborn&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Document + Key-Value&lt;/td&gt;
&lt;td&gt;Auto-scaling, managed infrastructure&lt;/td&gt;
&lt;td&gt;Serverless apps, gaming, shopping carts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🎯 &lt;strong&gt;Elasticsearch — The Searchblade&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Search / Document&lt;/td&gt;
&lt;td&gt;Full-text search precision, analytics power&lt;/td&gt;
&lt;td&gt;App/site search, log analytics, observability&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🗿 &lt;strong&gt;HBase — The Ancient Colossus&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Wide-column&lt;/td&gt;
&lt;td&gt;Batch data processing, massive scale&lt;/td&gt;
&lt;td&gt;Telecom, financial archives, Hadoop ecosystems&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Final Word: Choose Your Champion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In this arena, there's no single victor—each fighter dominates their specific battlefield. Whether you're crafting a social empire, building lightning-fast apps, or managing global dataflows, there's a NoSQL warrior ready to fight for you.&lt;/p&gt;

&lt;p&gt;Who will be the winner? Who would you choose for your next data battle? Tell me in the comments!&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%2F31z16qkl2z0wbx9mcm2k.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%2F31z16qkl2z0wbx9mcm2k.png" alt="Image description" width="800" height="344"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  References of fighters:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.mongodb.com" rel="noopener noreferrer"&gt;MongoDB&lt;/a&gt;&lt;br&gt;
&lt;a href="https://cassandra.apache.org" rel="noopener noreferrer"&gt;Apache Cassandra&lt;/a&gt;&lt;br&gt;
&lt;a href="https://redis.io" rel="noopener noreferrer"&gt;Redis&lt;/a&gt;&lt;br&gt;
&lt;a href="https://neo4j.com" rel="noopener noreferrer"&gt;Neo4j&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.couchbase.com" rel="noopener noreferrer"&gt;Couchbase&lt;/a&gt;&lt;br&gt;
&lt;a href="https://aws.amazon.com/dynamodb" rel="noopener noreferrer"&gt;Amazon DynamoDB&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.elastic.co/elasticsearch" rel="noopener noreferrer"&gt;Elasticsearch&lt;/a&gt;&lt;br&gt;
&lt;a href="https://hbase.apache.org" rel="noopener noreferrer"&gt;Apache HBase&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Need more tech insights?
&lt;/h2&gt;

&lt;p&gt;Check out my &lt;a href="https://github.com/wallaceespindola" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; repo and my &lt;a href="https://www.linkedin.com/in/wallaceespindola" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; page. Slides &lt;a href="https://speakerdeck.com/wallacese/nosql-fighters-arena-the-battle-of-data-titans" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
Do you want to buy me a coffee to elevate my energy? You can do it &lt;a href="https://buymeacoffee.com/wallace.espindola" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

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

</description>
      <category>database</category>
      <category>developers</category>
      <category>dataengineering</category>
      <category>performance</category>
    </item>
    <item>
      <title>Test Python Code Like a Pro with Poetry, Tox, Nox and CI/CD</title>
      <dc:creator>Wallace Espindola</dc:creator>
      <pubDate>Mon, 06 Jan 2025 17:59:19 +0000</pubDate>
      <link>https://dev.to/wallaceespindola/test-python-code-like-a-pro-with-poetry-tox-nox-and-cicd-1i6p</link>
      <guid>https://dev.to/wallaceespindola/test-python-code-like-a-pro-with-poetry-tox-nox-and-cicd-1i6p</guid>
      <description>&lt;p&gt;Hey there!&lt;/p&gt;

&lt;p&gt;Got a Python project and need to make sure it works on every version of Python out there? Trust me, that can be a HUGE headache. But don't worry, I’ve got your back. In this guide, I’ll show you how to use &lt;strong&gt;Tox&lt;/strong&gt;, &lt;strong&gt;Nox&lt;/strong&gt; and &lt;strong&gt;CI/CD&lt;/strong&gt;, awesome tools, to test your code across multiple Python versions.&lt;/p&gt;

&lt;p&gt;And guess what? It’s easier than you think.&lt;/p&gt;

&lt;p&gt;By the time you’re done reading this, you’ll be running tests like a pro across Python 3.8 to 3.13. We’ll keep things simple, fun, and totally actionable. Sounds good? Let’s dive in.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Should You Even Care About Multi-Version Testing?
&lt;/h2&gt;

&lt;p&gt;Picture this: You write some cool Python code, and it works on your computer. But then, BAM! A user emails you, saying it breaks on Python 3.9. You try it, and sure enough, something’s off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because Python’s got all these versions, and each one has its quirks. If you don’t test your code on multiple versions, you’re flying blind.&lt;/p&gt;

&lt;p&gt;But the GOOD NEWS is, you don’t have to manually install a bunch of Python versions and run tests on each. That’s where Tox and Nox swoop in like superheroes. And CI/CD standout, of course.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Are Tox, Nox and CI/CD?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Let’s break it down:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tox&lt;/strong&gt;: Think of it as a robot that tests your code in different Python environments. It’s super organized and follows your instructions from a simple tox.ini file. You tell Tox what to do, and it does it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Nox&lt;/strong&gt;: It’s like Tox, but cooler in some ways. Why? Because instead of a config file, you get to write a Python script (noxfile.py). Want to add custom logic or conditions? Nox has your back.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CI/CD&lt;/strong&gt;: Imagine an automated assembly line for your code. Continuous Integration (CI) ensures that every change you make is automatically built and tested, catching issues early. Continuous Deployment (CD) takes it a step further by automatically deploying your tested code to production or staging environments. This seamless pipeline ensures that your software is always in a deployable state, reduces manual intervention, and accelerates the delivery of new features and fixes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So which one’s better? Honestly, it depends. If you like things neat and straightforward, go with Tox. If you’re the creative type and love flexibility, Nox is your jam. And if you want to automate your entire development workflow, from testing to deployment, CI/CD is the way to go.&lt;/p&gt;




&lt;h2&gt;
  
  
  Let’s Build Something Cool
&lt;/h2&gt;

&lt;p&gt;Here’s the deal: &lt;/p&gt;

&lt;p&gt;We’re gonna create a mini project with two simple functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add two numbers.&lt;/li&gt;
&lt;li&gt;Subtract one number from another.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll write some tests to make sure they work, and then we’ll use Tox, Nox and CI/CD to test them on Python versions from 3.8 to 3.13.&lt;/p&gt;

&lt;p&gt;Sounds fun, right?&lt;/p&gt;

&lt;p&gt;Here’s the file structure we’re working with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="err"&gt;tox-nox-python-tests/&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="err"&gt;.github&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="err"&gt;workflows&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;      &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="err"&gt;python-tests-matrix.yml&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="err"&gt;tox_nox_python_test_automation/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="err"&gt;__init__.py&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="err"&gt;main.py&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="err"&gt;calculator.py&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="err"&gt;tests/&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="err"&gt;__init__.py&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;   &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="err"&gt;test_calculator.py&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="err"&gt;.gitlab-ci.yml&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="err"&gt;pyproject.toml&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="err"&gt;tox.ini&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="err"&gt;noxfile.py&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="err"&gt;README.md&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 1: Write the Code
&lt;/h2&gt;

&lt;p&gt;Here’s our &lt;strong&gt;calculator.py&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;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Returns the sum of two numbers.&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;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Returns the difference of two numbers.&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;a&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple, right? Let’s keep it that way.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Write Some Tests
&lt;/h2&gt;

&lt;p&gt;Time to make sure our code works. Here’s our &lt;strong&gt;test_calculator.py&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tox_nox_python_test_automation.calculator&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subtract&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.mark.parametrize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a, b, expected&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="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&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_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.mark.parametrize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a, b, expected&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="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&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_subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re using &lt;strong&gt;pytest&lt;/strong&gt;, a testing tool that’s basically the MVP of Python testing. If you’ve never used it, don’t sweat it, it’s super easy to pick up.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Manage Dependencies with Poetry
&lt;/h2&gt;

&lt;p&gt;Okay, so how do we make sure everyone working on this project uses the same dependencies? We use Poetry, which is like a supercharged requirements.txt file.&lt;/p&gt;

&lt;p&gt;Here’s what our &lt;strong&gt;pyproject.toml&lt;/strong&gt; looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;tool.poetry&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="s"&gt;name = "tox_nox_python_tests"&lt;/span&gt;
&lt;span class="s"&gt;version = "0.1.0"&lt;/span&gt;
&lt;span class="s"&gt;description = "Testing with multiple Python versions using Tox, Nox and CI/CD."&lt;/span&gt;
&lt;span class="s"&gt;authors = ["Wallace Espindola &amp;lt;wallace.espindola@gmail.com&amp;gt;"]&lt;/span&gt;
&lt;span class="s"&gt;license = "MIT"&lt;/span&gt;

&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;tool.poetry.dependencies&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="s"&gt;python = "^3.8"&lt;/span&gt;
&lt;span class="s"&gt;pytest = "^8.3"&lt;/span&gt;
&lt;span class="s"&gt;nox = "^2024.10.9"&lt;/span&gt;
&lt;span class="s"&gt;tox = "^4.23.2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To install everything, just run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4: Run unit tests with Pytest
&lt;/h2&gt;

&lt;p&gt;You can run the basic unit tests this way:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;And will see a standard unit test running output.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Test with Tox
&lt;/h2&gt;

&lt;p&gt;Tox is all about automation. Here’s our &lt;strong&gt;tox.ini&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;tox&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="s"&gt;envlist = py38, py39, py310, py311, py312, py313&lt;/span&gt;

&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;testenv&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="s"&gt;allowlist_externals = poetry&lt;/span&gt;
&lt;span class="s"&gt;commands_pre =&lt;/span&gt;
    &lt;span class="s"&gt;poetry install --no-interaction --no-root&lt;/span&gt;
&lt;span class="s"&gt;commands =&lt;/span&gt;
    &lt;span class="s"&gt;poetry run pytest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run Tox with one command:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;And boom! Tox will test your code across every version of Python listed. See and example output here:&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%2Finyuzsuscumbktvqxsvo.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%2Finyuzsuscumbktvqxsvo.png" alt="Image description" width="595" height="192"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Test with Nox
&lt;/h2&gt;

&lt;p&gt;Want more control? Nox lets you get creative. Here’s our &lt;strong&gt;noxfile.py&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;nox&lt;/span&gt;

&lt;span class="nd"&gt;@nox.session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;python&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;3.8&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;3.9&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;3.10&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;3.11&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;3.12&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;3.13&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;tests&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;install&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;poetry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;poetry&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;install&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;--no-interaction&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;--no-root&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pytest&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;Run Nox with:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Now you’ve got full flexibility to add logic, skip environments, or do whatever else you need. See and example output here:&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%2F6dcxv1hmq9pqoy3oupmy.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%2F6dcxv1hmq9pqoy3oupmy.png" alt="Image description" width="800" height="282"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Automate with CI/CD
&lt;/h2&gt;

&lt;p&gt;Why stop at local testing? Let’s set this up to run automatically on GitHub Actions and GitLab CI/CD.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Actions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here’s a workflow file &lt;strong&gt;.github/workflows/python-tests.yml&lt;/strong&gt; for Tox or Nox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Python Tests Tox (or Nox)&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;develop&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Python&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PYTHON_VERSION}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Poetry&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pip install poetry&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;poetry install --no-interaction --no-root&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Tests with Tox (or Nox)&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;poetry run tox (or nox)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And another example for parallel matrix (pure CI/CD):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Python Tests with Matrix&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;develop&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;3.8&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;3.9&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;3.10&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;3.11&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;3.12&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;3.13&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Python ${{ matrix.python-version }}&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.python-version }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Poetry&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pip install poetry&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;poetry install --no-interaction --no-root&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Tests with Pytest&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;poetry run pytest --verbose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;GitLab CI/CD&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here’s an example &lt;strong&gt;.gitlab-ci.yml&lt;/strong&gt; for Tox or Nox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;

&lt;span class="na"&gt;tox_tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python:${PYTHON_VERSION}&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;PIP_CACHE_DIR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$CI_PROJECT_DIR/.cache/pip"&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip install poetry&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;poetry install --no-interaction --no-root&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;poetry run tox (or nox)&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cache/pip/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here’s an example for a parallel matrix tests (pure CI/CD):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;

&lt;span class="na"&gt;matrix_tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python:${PYTHON_VERSION}&lt;/span&gt;
  &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;PIP_CACHE_DIR&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;$CI_PROJECT_DIR/.cache/pip"&lt;/span&gt;
  &lt;span class="na"&gt;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pip install poetry&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;poetry install --no-interaction --no-root&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;poetry run pytest --verbose&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cache/pip/&lt;/span&gt;
  &lt;span class="na"&gt;parallel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;PYTHON_VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.8"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.9"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.10"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.11"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.12"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.13"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Let’s Wrap It Up
&lt;/h2&gt;

&lt;p&gt;You did it! You now know how to test Python code across multiple versions using Tox, Nox, and Poetry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here’s what to remember:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tox is your go-to for simple, automated testing.&lt;/li&gt;
&lt;li&gt;Nox gives you the freedom to customize.&lt;/li&gt;
&lt;li&gt;Poetry makes managing dependencies a breeze.&lt;/li&gt;
&lt;li&gt;CI/CD ensures your tests run automatically.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  References, of course
&lt;/h2&gt;

&lt;p&gt;This project uses &lt;strong&gt;Tox&lt;/strong&gt;, &lt;strong&gt;Nox&lt;/strong&gt;, &lt;strong&gt;Poetry&lt;/strong&gt;, and &lt;strong&gt;Pytest&lt;/strong&gt; for test automation. For detailed documentation, take a look at:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tox.wiki/en/" rel="noopener noreferrer"&gt;Tox Documentation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://nox.thea.codes/en/stable/" rel="noopener noreferrer"&gt;Nox Documentation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://python-poetry.org/" rel="noopener noreferrer"&gt;Poetry Documentation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.pytest.org/en/stable/" rel="noopener noreferrer"&gt;Pytest Documentation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.gitlab.com/" rel="noopener noreferrer"&gt;GitLab CI/CD Documentation&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;GitHub Actions CI/CD Documentation&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Need the full code and examples?
&lt;/h2&gt;

&lt;p&gt;Check out the repo on &lt;a href="https://github.com/wallaceespindola/tox-nox-python-tests" rel="noopener noreferrer"&gt;GitHub: tox-nox-python-tests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For other interesting subjects and technical discussions, check my &lt;a href="https://www.linkedin.com/in/wallaceespindola" rel="noopener noreferrer"&gt;LinkedIn page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For some slides, take a look &lt;a href="https://speakerdeck.com/wallacese" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Do you want to buy me a coffee to elevate my energy? You can do it &lt;a href="https://buymeacoffee.com/wallace.espindola" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;br&gt;&lt;br&gt;
Now go out there and make your Python projects bulletproof! 🚀&lt;/p&gt;

</description>
      <category>python</category>
      <category>testing</category>
      <category>tox</category>
      <category>nox</category>
    </item>
    <item>
      <title>Python Multithreading: Unlocking Concurrency for Better Performance</title>
      <dc:creator>Wallace Espindola</dc:creator>
      <pubDate>Fri, 17 May 2024 21:23:43 +0000</pubDate>
      <link>https://dev.to/wallaceespindola/python-multithreading-unlocking-concurrency-4gho</link>
      <guid>https://dev.to/wallaceespindola/python-multithreading-unlocking-concurrency-4gho</guid>
      <description>&lt;p&gt;Hey, let's dive into the world of Python multithreading! Whether you're an intermediate or advanced developer, or maybe even coming from another programming language, mastering this will boost your skills and your applications' performance. Ready to tackle it? Let's go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Multithreading?
&lt;/h2&gt;

&lt;p&gt;Multithreading can seem daunting, but it's a game-changer for performing multiple operations at once, especially when dealing with I/O-bound or high-latency operations. Think of it as hiring more workers for your shop when it gets busy. Each worker handles a different customer simultaneously, speeding up service. That's what threads do for your programs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Your Workshop – The Basics
&lt;/h2&gt;

&lt;p&gt;First, let's set up our environment. Python provides a built-in module for threading called threading. Here’s how you get started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_something&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;Look, I&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;m running in a thread!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Create a thread
&lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;do_something&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Start the thread
&lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Wait for the thread to complete
&lt;/span&gt;&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&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;All done!&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;This basic setup creates and starts a thread that runs a function. The join() method is crucial, as it tells Python to wait for the thread to complete before moving on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Healthy Threading
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Keep the GIL in mind:&lt;/strong&gt; Python’s Global Interpreter Lock (GIL) allows only one thread to execute at a time in a single process, which can be a bottleneck. But don’t worry! This mainly affects CPU-bound tasks. For I/O-bound tasks, threading can still be very beneficial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Use ThreadPoolExecutor:&lt;/strong&gt; This is a fantastic tool from the concurrent.futures module. It simplifies managing a pool of threads and handling tasks. Here’s how you can use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;concurrent.futures&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&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;Processing &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;n&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;with&lt;/span&gt; &lt;span class="nc"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example creates a pool of three threads, each processing a part of a range of numbers. Using ThreadPoolExecutor not only simplifies thread management but also handles task distribution and collection elegantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Don’t share state if possible:&lt;/strong&gt; Sharing data between threads can lead to data corruption and other nasty bugs if not handled properly. If you must share data, use locks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Lock&lt;/span&gt;

&lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;shared_resource&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;safely_add&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="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;shared_resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&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="n"&gt;threads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;safely_add&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&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;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&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;thread&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&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;thread&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;threads&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&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="n"&gt;shared_resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each thread in this example safely adds an item to a shared list, thanks to the lock that ensures only one thread modifies the list at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Not to Use Multithreading
&lt;/h2&gt;

&lt;p&gt;Gotcha! While multithreading is powerful, it’s not a silver bullet. For CPU-bound tasks that require heavy computation and little I/O, consider using multiprocessing or asynchronous programming (like &lt;a href="https://docs.python.org/3/library/asyncio.html" rel="noopener noreferrer"&gt;asyncio&lt;/a&gt;) instead. These tools can bypass the GIL and truly run code in parallel, maximizing CPU usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging Multithreaded Applications
&lt;/h2&gt;

&lt;p&gt;Yes, it can be tricky! Here are a couple of quick tips:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Logging is your friend:&lt;/strong&gt; Use Python’s logging module to help track down what each thread is doing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Use debuggers that understand threads:&lt;/strong&gt; Tools like IntelliJ, PyCharm and VS Code have debuggers that can handle threading well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wrapping Up with a Real-World Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine you're building a web scraper that downloads content from multiple URLs. Multithreading can significantly speed up this process since network requests are I/O-bound. Here’s a sketch of how you might set this up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;concurrent.futures&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&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;url&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;

&lt;span class="n"&gt;urls&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;https://example.com&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;https://example.org&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;https://example.net&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urls&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;Fetched the content of all URLs!&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;This example fetches data from multiple URLs in parallel. Each thread handles downloading the content from one URL, which is typically a slow operation due to network latency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep Learning and Experimenting
&lt;/h2&gt;

&lt;p&gt;Multithreading is a vast topic, and mastering it can take some time. Keep experimenting with different scenarios and tweak your approach as you learn more about Python's capabilities and limitations. Remember, the more you practice, the better you’ll get!&lt;/p&gt;

&lt;p&gt;Now, why not try implementing a multithreaded solution in your next project? Dive in, break things, fix them, and learn. Go ahead!&lt;/p&gt;

&lt;p&gt;You may check this &lt;a href="https://github.com/wallaceespindola/python-multithreading" rel="noopener noreferrer"&gt;python-multithreading&lt;/a&gt; project I created on &lt;a href="https://github.com/wallaceespindola" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; with examples and tests, and also, for other interesting technical discussions and subjects, check my &lt;a href="https://www.linkedin.com/in/wallaceespindola" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; page. Happy coding!&lt;/p&gt;

</description>
      <category>python</category>
      <category>threading</category>
      <category>programming</category>
      <category>learning</category>
    </item>
    <item>
      <title>Best API Performance Testing Tools in 2024</title>
      <dc:creator>Wallace Espindola</dc:creator>
      <pubDate>Wed, 31 Jan 2024 19:54:00 +0000</pubDate>
      <link>https://dev.to/wallaceespindola/best-api-performance-testing-tools-in-2024-3n92</link>
      <guid>https://dev.to/wallaceespindola/best-api-performance-testing-tools-in-2024-3n92</guid>
      <description>&lt;p&gt;In the dynamic world of software development, ensuring the performance and reliability of APIs is primordial. As applications grow in complexity and scale, developers and QA engineers turn to a variety of processes and tools to rigorously test API performance under various conditions. This article explores some of the most common processes and tools in the market, offering insights into their capabilities, ease of use, and how they can be integrated into development workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  First of all, how and what to test
&lt;/h2&gt;

&lt;p&gt;Testing your API performance involves several key steps and tools designed to evaluate its responsiveness, throughput, stability under load, and more. Here's a structured approach to doing so:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Define Performance Goals&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Latency&lt;/strong&gt;: Maximum acceptable time for the API to respond.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throughput&lt;/strong&gt;: Number of requests per second the API should handle.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency&lt;/strong&gt;: Number of simultaneous connections the API must support.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Availability&lt;/strong&gt;: Expected uptime percentage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Understand Your API Workload&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identify the Most Common Operations&lt;/strong&gt;: Pinpoint the most frequently used endpoints and functions within your API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Determine Peak Usage Times and Patterns&lt;/strong&gt;: Analyze when your API experiences the highest traffic and the nature of this usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analyze User Demographics and Geographies&lt;/strong&gt;: Understand where your users are located and how geographic distribution affects API usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identify Dependent Systems and Integrations&lt;/strong&gt;: Know which external or internal systems interact with your API and how these dependencies affect performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor Mobile vs. Desktop Usage&lt;/strong&gt;: Be aware of the differences in API usage between mobile and desktop clients, as they can have different performance characteristics.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Set Up Your Testing Environment&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Similarity to production&lt;/strong&gt;: Ensure the testing environment closely mimics the production environment to get accurate results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment isolation&lt;/strong&gt;: Isolate the test environment to avoid impacting production systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make it reliable&lt;/strong&gt;: Avoid instable server environments where tests can be impacted by external variables.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Create Test Cases&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Baseline Testing&lt;/strong&gt;: Determine how the API performs under normal conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load Testing&lt;/strong&gt;: Gradually increase the load on the API until it reaches the expected maximum.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stress Testing&lt;/strong&gt;: Push the API beyond its expected maximum until it breaks to understand its upper limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spike Testing&lt;/strong&gt;: Test the API with sudden increases in load to simulate real-world spikes in usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Endurance Testing&lt;/strong&gt;: Evaluate how the API performs under a sustained load over a long period.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Monitor and Measure Performance&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Response Time&lt;/strong&gt;: Time taken for the API to respond.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Throughput&lt;/strong&gt;: Number of requests handled per second.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Rate&lt;/strong&gt;: Percentage of failed requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource Utilization&lt;/strong&gt;: CPU, memory, disk, and network usage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Analyze Results and Optimize&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identify bottlenecks and limitations in your API and infrastructure.&lt;/li&gt;
&lt;li&gt;Optimize code, database queries, server configuration, etc.&lt;/li&gt;
&lt;li&gt;Repeat tests to measure improvements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Continuous Monitoring and Testing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Integrate performance testing into your continuous integration/continuous deployment (CI/CD) pipeline.&lt;/li&gt;
&lt;li&gt;Regularly monitor API performance in production to detect and address new performance issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Second, choose the right tools
&lt;/h2&gt;

&lt;p&gt;Here are some comprehensive and widely used performance and load testing tools for APIs to excel in 2024.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://jmeter.apache.org/" rel="noopener noreferrer"&gt;JMeter&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Apache JMeter stands out for its robustness and flexibility, catering to a wide range of testing needs from load testing to stress and spike testing. While its graphical user interface (GUI) enhances accessibility, making it suitable for both technical and non-technical users, mastering its advanced features can require a deeper dive. JMeter's ability to simulate heavy loads across multiple machines makes it a go-to for large-scale performance testing. The tool enjoys strong community support, ensuring an abundance of resources and documentation for users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Originally designed for API development, Postman has evolved to include performance testing features. It's celebrated for its user-friendly interface, appealing to developers and testers at all levels. Although its performance testing capabilities are more rudimentary compared to dedicated tools, it excels in API development and testing workflows. Postman's community is vast, providing extensive support and resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://locust.io/" rel="noopener noreferrer"&gt;Locust&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Locust differentiates itself with a code-driven approach, allowing testers to write tests in Python to simulate millions of users. This flexibility is particularly appealing to developers comfortable with scripting. Its distributed testing capability is robust, backed by real-time monitoring through a web UI. While detailed analysis may require external tools, Locust's scalability is a key strength. The community around Locust is actively growing, contributing to its development and support. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://gatling.io/" rel="noopener noreferrer"&gt;Gatling&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Gatling impresses with high-performance tests and detailed reporting, thanks to its Scala-based Domain-Specific Language (DSL). Although learning Scala might be a barrier for some, the investment pays off with Gatling's efficient and scalable performance testing capabilities. Its comprehensive reports offer in-depth insights into application behavior under load. Gatling has a supportive community, providing ample learning resources. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://k6.io/" rel="noopener noreferrer"&gt;Grafana k6&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
Grafana k6 is a modern tool designed with developers in mind, offering a JavaScript environment for scripting tests. It's optimized for minimal resource consumption while delivering powerful performance testing features. k6's integration with Grafana for advanced data visualization and analysis stands out, providing clear insights into performance metrics. The tool's community is rapidly expanding, offering strong support and documentation. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enhancing Performance Testing with Monitoring and CI/CD Integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Beyond testing tools, monitoring solutions like &lt;a href="https://newrelic.com/" rel="noopener noreferrer"&gt;New Relic&lt;/a&gt;, &lt;a href="https://www.datadoghq.com/" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt;, &lt;a href="https://prometheus.io/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt;, &lt;a href="https://www.splunk.com/" rel="noopener noreferrer"&gt;Splunk&lt;/a&gt;, and &lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt; play a critical role in real-time performance monitoring, offering insights that guide optimization efforts.&lt;/p&gt;

&lt;p&gt;Incorporating these tests into CI/CD pipelines automates performance testing, ensuring continuous delivery of high-quality software. Tools like &lt;a href="https://www.jenkins.io/" rel="noopener noreferrer"&gt;Jenkins&lt;/a&gt;, &lt;a href="https://docs.gitlab.com/ee/ci/" rel="noopener noreferrer"&gt;GitLab CI&lt;/a&gt;, &lt;a href="https://circleci.com/" rel="noopener noreferrer"&gt;CircleCI&lt;/a&gt;, &lt;a href="https://www.travis-ci.com/" rel="noopener noreferrer"&gt;Travis CI&lt;/a&gt;, &lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt;, &lt;a href="https://www.jetbrains.com/teamcity/" rel="noopener noreferrer"&gt;JetBrains TeamCity&lt;/a&gt;, and &lt;a href="https://www.atlassian.com/software/bamboo" rel="noopener noreferrer"&gt;Bamboo&lt;/a&gt; integrate seamlessly with testing workflows, enabling automated builds, tests, and deployments. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The choice of API performance testing tools and processes depends on specific project requirements, team expertise, and the scale of testing needed. Whether it's the comprehensive testing capabilities of JMeter and Gatling, the developer-friendly nature of Locust and k6, or the API development integration of Postman, each tool offers unique strengths. Coupled with effective monitoring and CI/CD integration, these tools empower teams to deliver robust, high-performing applications that stand the test of user expectations and system demands.&lt;/p&gt;

&lt;p&gt;For some interesting performance tests topics, you may take a look on my &lt;a href="https://github.com/wallaceespindola" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;, and/or on my &lt;a href="https://www.linkedin.com/in/wallaceespindola/" rel="noopener noreferrer"&gt;LinkedIn profile&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>api</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
