<?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: Naman Verma</title>
    <description>The latest articles on DEV Community by Naman Verma (@naman_verma_b8b6f8ad1fa82).</description>
    <link>https://dev.to/naman_verma_b8b6f8ad1fa82</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3594012%2Fa24422c2-ad9e-475b-aff3-d69e335028ba.png</url>
      <title>DEV Community: Naman Verma</title>
      <link>https://dev.to/naman_verma_b8b6f8ad1fa82</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/naman_verma_b8b6f8ad1fa82"/>
    <language>en</language>
    <item>
      <title>“Real Developers Test in Production.” But Should They?</title>
      <dc:creator>Naman Verma</dc:creator>
      <pubDate>Thu, 22 Oct 2026 12:09:23 +0000</pubDate>
      <link>https://dev.to/naman_verma_b8b6f8ad1fa82/real-developers-test-in-production-but-should-they-1a1j</link>
      <guid>https://dev.to/naman_verma_b8b6f8ad1fa82/real-developers-test-in-production-but-should-they-1a1j</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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmd0rtk81cpg0w9e4w7v6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmd0rtk81cpg0w9e4w7v6.jpg" alt="test-in-prod-meme" width="430" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You've probably heard this joke before. It pokes fun at developers who skip testing, push code straight to production, and let real users discover the bugs.&lt;/p&gt;

&lt;p&gt;But there's a reason the joke exists. Software that works on a developer's machine can behave very differently when real requests, databases, and multiple services get involved.&lt;/p&gt;

&lt;p&gt;A broken update endpoint could corrupt customer data. An unhandled error could take down a critical feature. A bad deployment could turn a routine release into hours of debugging, incident response, and frustrated users.&lt;/p&gt;

&lt;p&gt;And fixing the code is only part of the problem. Someone has to identify what broke, reproduce the issue, assess its impact, roll back or deploy a fix, and verify that the same bug doesn't return. Depending on the organization, that work can involve developers, QA engineers, SREs, and incident responders.&lt;/p&gt;

&lt;p&gt;So, should developers test in production?&lt;/p&gt;

&lt;p&gt;Their are different types of Production testing which are done like A/B testing, Canary Releases, Feature Flags, Shadow Testing, Smoke testing etc.&lt;/p&gt;

&lt;p&gt;But these testings are done for a specific purpose and that is to test the application in a production environment. But these testings are not enough to catch all the bugs that our existing tests miss before they affect real users.&lt;/p&gt;

&lt;p&gt;Let's build a small application, break it deliberately, explore the testing techniques that help prevent these failures, and see how Keploy can turn recorded API interactions into reusable regression tests so we dont have to write the test cases and guess what scenarios to test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample Application Setup
&lt;/h2&gt;

&lt;p&gt;We have a sample Bookmark application with Go, Echo and PostgreSQL. It has all the CRUD operations for bookmarks along with a simple UI to interact with the application.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://go.dev/doc/install" rel="noopener noreferrer"&gt;Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/desktop/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://keploy.io/docs/server/installation/" rel="noopener noreferrer"&gt;Keploy CLI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Clone and run this sample application locally using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Namanv0509/bookmark.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;bookmark

go mod download
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To start the application run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the health of endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8080/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the following command to stop it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Let's Break Something First
&lt;/h2&gt;

&lt;p&gt;We'll use bookmark, a simple bookmark manager built with Go, Echo, and PostgreSQL.&lt;/p&gt;

&lt;p&gt;Users can save bookmarks, add notes and tags, view their saved links, update them, and delete them. The application has a small browser interface and a REST API.&lt;/p&gt;

&lt;p&gt;Start the application locally and Open &lt;code&gt;http://localhost:8080&lt;/code&gt; and create a bookmark. You can also interact with the API directly.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu5ur8s9d44wnmah1b4pn.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu5ur8s9d44wnmah1b4pn.png" alt="save-bookmark" width="800" height="437"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8080/links &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "url": "https://www.google.com/",
    "title": "Google Search",
    "notes": "Sounds like a good search engine. It helps me find good blogs like this.",
    "tags": ["Search Engine"]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The application should save the bookmark and return its details.&lt;/p&gt;

&lt;p&gt;Now let's imagine we're making a small improvement to the update endpoint. We change its SQL query, test that the application starts, and deploy the new version without checking whether the update behavior still works.&lt;/p&gt;

&lt;p&gt;Here's the mistake we introduce:&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;UPDATE&lt;/span&gt; &lt;span class="n"&gt;links&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;notes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The title and notes parameters are swapped. We intended to assign the title from $2 and the notes from $3, but accidentally did the opposite.&lt;/p&gt;

&lt;p&gt;Let's send an update request with distinct values to see the effect.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fck24irarxeegylig7sop.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fck24irarxeegylig7sop.png" alt="bug-bookmark" width="799" height="432"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; PUT http://localhost:8080/links/1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "url": "https://www.google.com/",
    "title": "Title",
    "notes": "Notes",
    "tags": ["updated"]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The endpoint still return a successful response. The application is running and everything looks fine. Yet the bookmark's title can now become notes.&lt;/p&gt;

&lt;p&gt;We've introduced a bug without necessarily causing a crash.&lt;/p&gt;

&lt;p&gt;Imagine the same mistake in a production application. Users might save incorrect information, downstream services might consume the corrupted data, and the problem might go unnoticed until someone reports it.&lt;/p&gt;

&lt;p&gt;Let's imagine a different mistake: a database connection failure that causes requests to time out. That could make a feature unavailable, potentially affecting every user who relies on it.&lt;/p&gt;

&lt;p&gt;These are different failure modes, but they reveal the same problem: &lt;strong&gt;a successful deployment doesn't guarantee correct behavior.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does “Testing in Production” Actually Mean?
&lt;/h2&gt;

&lt;p&gt;The phrase gets used to describe several different practices, but they aren't interchangeable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skipping tests and deploying directly
&lt;/h3&gt;

&lt;p&gt;This is the reckless interpretation of the joke: code goes straight to production without sufficient verification.&lt;/p&gt;

&lt;p&gt;It might appear faster initially, but failures can become expensive. Developers may have to investigate unfamiliar production states, QA teams may need to reproduce issues, and operations teams may have to mitigate an outage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing in a staging environment
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbanmmpq0j0f1nn7u5drt.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbanmmpq0j0f1nn7u5drt.png" alt="Staging" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A staging environment is a separate environment intended to resemble production. Teams can deploy a release candidate there and run tests before exposing it to users.&lt;/p&gt;

&lt;p&gt;Staging is useful, but it isn't a perfect copy of production. Data volumes, configurations, integrations, and traffic patterns can differ.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing in production deliberately
&lt;/h3&gt;

&lt;p&gt;Some teams perform controlled tests in production because certain conditions are difficult to reproduce elsewhere.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Canary releases&lt;/strong&gt; send a small portion of real traffic to a new version before a wider rollout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature flags&lt;/strong&gt; let teams enable or disable features for selected users and gradually expand access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A/B testing&lt;/strong&gt; compares different versions of a feature with different user groups to measure outcomes such as engagement or conversion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shadow testing&lt;/strong&gt; mirrors real requests to a candidate version so its behavior can be evaluated without using its responses for normal user-facing results.results.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Different Layers of Software Testing
&lt;/h2&gt;

&lt;p&gt;Different tests answer different questions. A practical testing strategy combines them rather than relying on a single tool.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Question it answers&lt;/th&gt;
&lt;th&gt;bookmark example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unit testing&lt;/td&gt;
&lt;td&gt;Does this piece of logic work?&lt;/td&gt;
&lt;td&gt;Does URL validation reject &lt;code&gt;ftp://&lt;/code&gt; URLs?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integration testing&lt;/td&gt;
&lt;td&gt;Do components work together?&lt;/td&gt;
&lt;td&gt;Can the API save a bookmark in PostgreSQL?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API testing&lt;/td&gt;
&lt;td&gt;Does the HTTP interface behave correctly?&lt;/td&gt;
&lt;td&gt;Does an invalid request return the expected status and response?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;End-to-end testing&lt;/td&gt;
&lt;td&gt;Does a complete user workflow work?&lt;/td&gt;
&lt;td&gt;Can a user create a bookmark through the browser and see it in the list?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regression testing&lt;/td&gt;
&lt;td&gt;Did a change break existing behavior?&lt;/td&gt;
&lt;td&gt;Does updating a bookmark still preserve its title and notes?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Load testing&lt;/td&gt;
&lt;td&gt;How does the application behave under traffic?&lt;/td&gt;
&lt;td&gt;Can the list endpoint handle concurrent requests?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These categories can overlap. For example, an API test that exercises the actual database is also an integration test. Regression testing describes the goal of detecting unintended changes, not a particular test level.&lt;/p&gt;

&lt;p&gt;Let's look at what each approach gives us.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unit Testing: Check the Small Pieces First
&lt;/h2&gt;

&lt;p&gt;A unit test checks a small, isolated piece of code.&lt;/p&gt;

&lt;p&gt;For our bookmark application, we want to verify that the validation logic accepts valid HTTP URLs, rejects unsupported schemes, and requires a title. The test cases are already available in &lt;code&gt;main_test.go&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Run the following commands from your project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; ./...
go &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; ./...
go &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-cover&lt;/span&gt; ./...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first command runs the tests, the second displays individual test results, and the third reports code coverage.&lt;/p&gt;

&lt;p&gt;You should see output similar to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=== RUN   TestValidateInput
=== RUN   TestValidateInput/valid_input
=== RUN   TestValidateInput/missing_title
=== RUN   TestValidateInput/unsupported_scheme
=== RUN   TestValidateInput/missing_URL
--- PASS: TestValidateInput (0.00s)
PASS
ok      linkvault       0.110s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All four test cases pass, confirming that our validation logic handles these inputs as expected. However, our overall code coverage is only 7% because we've tested just a small part of the application.&lt;/p&gt;

&lt;p&gt;Unit tests help us verify individual pieces of logic, but they don't tell us whether our HTTP handlers and SQL queries work correctly together.&lt;/p&gt;

&lt;p&gt;For that, we need to test how the application interacts with its dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependencies: What Does Your Application Rely On?
&lt;/h2&gt;

&lt;p&gt;A dependency is something your code relies on to perform its job.&lt;/p&gt;

&lt;p&gt;bookmark has dependencies at several levels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Echo handles HTTP routing and middleware.&lt;/li&gt;
&lt;li&gt;A PostgreSQL driver lets Go communicate with the database.&lt;/li&gt;
&lt;li&gt;PostgreSQL stores bookmark data.&lt;/li&gt;
&lt;li&gt;The frontend depends on the API returning data in the expected format.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some dependencies are libraries. Others are external systems that must be running and correctly configured.&lt;/p&gt;

&lt;p&gt;This distinction matters when testing. Our validation function can run without PostgreSQL, but creating a bookmark requires a database connection, a valid schema, and a working SQL query.&lt;/p&gt;

&lt;p&gt;When a test fails, we want to know whether our own logic is wrong or a dependency is unavailable or behaving differently than expected.&lt;/p&gt;

&lt;p&gt;One way to investigate this is to test with the real dependency. Another is to replace it with a test double.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration and API Testing: Bring the Pieces Together
&lt;/h2&gt;

&lt;p&gt;Let's return to the running application.&lt;/p&gt;

&lt;p&gt;An integration test can send a request to bookmark, let the actual handler execute its SQL query, and verify that PostgreSQL stores the expected data.&lt;/p&gt;

&lt;p&gt;An API test focuses on the behavior exposed through HTTP. We might check that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a valid bookmark returns the expected response.&lt;/li&gt;
&lt;li&gt;An invalid URL produces a validation error.&lt;/li&gt;
&lt;li&gt;Retrieving an unknown ID returns the appropriate not-found response.&lt;/li&gt;
&lt;li&gt;Updating a bookmark changes its fields correctly.&lt;/li&gt;
&lt;li&gt;Deleting a bookmark removes it from subsequent results.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8080/links &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "url": "https://keploy.io",
    "title": "Keploy",
    "notes": "API testing tutorial",
    "tags": ["testing"]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then inspect the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8080/links
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These requests exercise more of the real application than our isolated unit tests.&lt;/p&gt;

&lt;p&gt;The problem is that manually repeating these requests after every code change quickly becomes tedious. We also need to ensure the same scenarios are tested consistently.&lt;/p&gt;

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

&lt;p&gt;How do we know a code change hasn't broken behavior that already worked?&lt;/p&gt;

&lt;p&gt;That's where regression testing comes in. It helps us verify that existing functionality continues to behave as expected after changes to the code.&lt;/p&gt;

&lt;p&gt;Their are severl tools we can use for it like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/keploy/keploy" rel="noopener noreferrer"&gt;Keploy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/opendiffy/diffy" rel="noopener noreferrer"&gt;Diffy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pact-foundation" rel="noopener noreferrer"&gt;Pact&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For our demo, we'll use Keploy to record API interactions and replay them after code changes. This lets us catch unexpected changes in application behavior without manually writing every test case.&lt;/p&gt;

&lt;p&gt;Keploy can also mock supported dependency interactions during replay, reducing the need to run every dependency.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Record the API interactions
&lt;/h3&gt;

&lt;p&gt;Start Keploy's recording workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;keploy record &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"docker compose up --build"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--container-name&lt;/span&gt; &lt;span class="s2"&gt;"linkvault-app"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--build-delay&lt;/span&gt; 60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your Keploy logs should look something like this in &lt;code&gt;keploy-logs.txt&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;🐰 Keploy: 2026-10-22T12:00:59.071063+05:30   [34mINFO[0m   Starting Keploy &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"3.8.79"&lt;/span&gt;, &lt;span class="s2"&gt;"apiServerURL"&lt;/span&gt;: &lt;span class="s2"&gt;"https://api.keploy.io"&lt;/span&gt;, &lt;span class="s2"&gt;"inDocker"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
🐰 Keploy: 2026-10-22T12:01:01.555659+05:30   [34mINFO[0m   AppName &lt;span class="k"&gt;in &lt;/span&gt;config &lt;span class="o"&gt;(&lt;/span&gt;bookmark&lt;span class="o"&gt;)&lt;/span&gt; does not match current directory name &lt;span class="o"&gt;(&lt;/span&gt;linkvault&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; using current directory name as appName
🐰 Keploy: 2026-10-22T12:01:01.556065+05:30   [34mINFO[0m   agent replay posture resolved   &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"replaying"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;, &lt;span class="s2"&gt;"cmd"&lt;/span&gt;: &lt;span class="s2"&gt;"record"&lt;/span&gt;, &lt;span class="s2"&gt;"mode"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;, &lt;span class="s2"&gt;"daemonset"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
🐰 Keploy: 2026-10-22T12:01:01.577954+05:30   [34mINFO[0m   Running application using a temporary Keploy-generated Docker Compose file &lt;span class="o"&gt;(&lt;/span&gt;will be cleaned up automatically&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"cmd"&lt;/span&gt;: &lt;span class="s2"&gt;"docker compose -f ./docker-compose-tmp.yaml up --abort-on-container-exit --exit-code-from app --build"&lt;/span&gt;, &lt;span class="s2"&gt;"composePath"&lt;/span&gt;: &lt;span class="s2"&gt;"docker-compose-tmp.yaml"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
🐰 Keploy: 2026-10-22T12:01:01.578072+05:30   [34mINFO[0m   Waiting &lt;span class="k"&gt;for &lt;/span&gt;keploy-agent to be ready &lt;span class="k"&gt;for &lt;/span&gt;docker compose...  &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"Agent-uri"&lt;/span&gt;: &lt;span class="s2"&gt;"http://localhost:61321/agent"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
🐰 Keploy: 2026-10-22T12:01:01.654583+05:30   [34mINFO[0m   Starting Application:   &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"executing_cmd"&lt;/span&gt;: &lt;span class="s2"&gt;"/bin/sh -c docker compose -f ./docker-compose-tmp.yaml up --abort-on-container-exit --exit-code-from app --build"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
🐰 Keploy: 2026-10-22T12:01:03.650059+05:30   [34mINFO[0m   Keploy agent is ready to record &lt;span class="nb"&gt;test &lt;/span&gt;cases and mocks.

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

&lt;/div&gt;



&lt;p&gt;With recording active, open &lt;code&gt;http://localhost:8080&lt;/code&gt; and exercise the application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a bookmark.&lt;/li&gt;
&lt;li&gt;Retrieve the bookmark list and an individual bookmark.&lt;/li&gt;
&lt;li&gt;Update its title and notes.&lt;/li&gt;
&lt;li&gt;Delete a bookmark.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffhcltypthqwgjaj6o8hr.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffhcltypthqwgjaj6o8hr.png" alt="update-bookmark" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We just performed an update to bookmark&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;🐰 Keploy: 2026-10-22T12:20:36.086862+05:30   [34mINFO[0m   🟠 Keploy has captured &lt;span class="nb"&gt;test &lt;/span&gt;cases &lt;span class="k"&gt;for &lt;/span&gt;the user&lt;span class="s1"&gt;'s application. {"path": "/Users/namanverma/Downloads/linkvault/keploy/test-set-0/tests", "testcase name": "post-links-1"}
🐰 Keploy: 2026-10-22T12:20:36.089196+05:30   [34mINFO[0m   🟠 Keploy has captured test cases for the user'&lt;/span&gt;s application. &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"path"&lt;/span&gt;: &lt;span class="s2"&gt;"/Users/namanverma/Downloads/linkvault/keploy/test-set-0/tests"&lt;/span&gt;, &lt;span class="s2"&gt;"testcase name"&lt;/span&gt;: &lt;span class="s2"&gt;"get-links-1"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
🐰 Keploy: 2026-10-22T12:30:01.330101+05:30   [34mINFO[0m   🟠 Keploy has captured &lt;span class="nb"&gt;test &lt;/span&gt;cases &lt;span class="k"&gt;for &lt;/span&gt;the user&lt;span class="s1"&gt;'s application. {"path": "/Users/namanverma/Downloads/linkvault/keploy/test-set-0/tests", "testcase name": "post-links-2"}
🐰 Keploy: 2026-10-22T12:30:01.333557+05:30   [34mINFO[0m   🟠 Keploy has captured test cases for the user'&lt;/span&gt;s application. &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"path"&lt;/span&gt;: &lt;span class="s2"&gt;"/Users/namanverma/Downloads/linkvault/keploy/test-set-0/tests"&lt;/span&gt;, &lt;span class="s2"&gt;"testcase name"&lt;/span&gt;: &lt;span class="s2"&gt;"get-links-2"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use distinct values for the title and notes so that incorrect updates are easier to detect.&lt;/p&gt;

&lt;p&gt;Stop recording with &lt;code&gt;Ctrl+C&lt;/code&gt;. Keploy captures the interactions and generates test cases according to the configured setup. keploy can be configured to mask senstitive information and handle noise.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Introduce a regression
&lt;/h3&gt;

&lt;p&gt;Let's deliberately introduce a bug in the update handler's HTTP response.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;main.go&lt;/code&gt;, locate the end of &lt;code&gt;updateLink()&lt;/code&gt;.&lt;br&gt;
Just before &lt;code&gt;return c.JSON(200, link)&lt;/code&gt; add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Intentional regression: swap title and notes in the response.&lt;/span&gt;
&lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Notes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Notes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simulates a response-mapping bug: the API returns the title as the notes and the notes as the title.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Replay the captured tests
&lt;/h3&gt;

&lt;p&gt;With the regression in place, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;keploy &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"docker compose up --build"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--container-name&lt;/span&gt; &lt;span class="s2"&gt;"linkvault-app"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--build-delay&lt;/span&gt; 60 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--delay&lt;/span&gt; 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Heads up: non-deterministic fields.&lt;/strong&gt; Fields like &lt;code&gt;created_at&lt;/code&gt;, &lt;code&gt;updated_at&lt;/code&gt; (set by &lt;code&gt;NOW()&lt;/code&gt;), and auto-generated IDs change on every run, keploy detects them and mark them as noise fields.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Keploy replays the captured requests against the rebuilt application and compares the resulting HTTP responses with the recorded responses.&lt;/p&gt;

&lt;p&gt;Because the values differ, Keploy should flag the response mismatch, provided those fields are included in its response comparison.&lt;/p&gt;

&lt;p&gt;and for sure you can see that in the test summary as well&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &amp;lt;&lt;span class="o"&gt;=========================================&amp;gt;&lt;/span&gt; 
  COMPLETE TESTRUN SUMMARY. 
        Total tests: 2
        Total &lt;span class="nb"&gt;test &lt;/span&gt;passed: 1
        Total &lt;span class="nb"&gt;test &lt;/span&gt;failed: 1
        Total &lt;span class="nb"&gt;time &lt;/span&gt;taken: &lt;span class="s2"&gt;"28.14 s"&lt;/span&gt;

        Test Suite Name         Total Test      Passed          Failed          Time Taken      Failed Testcases

        &lt;span class="s2"&gt;"test-set-0"&lt;/span&gt;            2               1               1               &lt;span class="s2"&gt;"28.14 s"&lt;/span&gt;       
&amp;lt;&lt;span class="o"&gt;=========================================&amp;gt;&lt;/span&gt; 

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

&lt;/div&gt;



&lt;p&gt;Your summary can vary depending on what and how many requests you have sent and your endpoint configs&lt;/p&gt;

&lt;p&gt;We never had to write the test cases and we could catch the regression by replaying our traffic recorded previously. We can automate this testing with CI/CD, we will cover that in next article. 🐰&lt;/p&gt;

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

&lt;p&gt;There's no single testing technique that catches every bug. Understanding when to use each approach helps us build more reliable software and catch problems before they reach production.&lt;/p&gt;

&lt;p&gt;In the upcoming articles, we'll dive deeper into different types of testing, how they work, and when to use them.&lt;/p&gt;

&lt;p&gt;If you'd like to explore further, here are two useful resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://microsoft.github.io/code-with-engineering-playbook/automated-testing/" rel="noopener noreferrer"&gt;Microsoft Engineering Playbook — Automated Testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://keploy.io/blog/technology" rel="noopener noreferrer"&gt;Keploy Blog — Technology&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Until next time, see you in the next one!&lt;/p&gt;

</description>
      <category>testing</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>go</category>
    </item>
  </channel>
</rss>
