<?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: Larry Gasik</title>
    <description>The latest articles on DEV Community by Larry Gasik (@gasik1417).</description>
    <link>https://dev.to/gasik1417</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%2F2979058%2F0c062e03-adcf-4e45-b54a-cbacadaf336b.png</url>
      <title>DEV Community: Larry Gasik</title>
      <link>https://dev.to/gasik1417</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gasik1417"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Larry Gasik</dc:creator>
      <pubDate>Mon, 10 Aug 2026 18:49:35 +0000</pubDate>
      <link>https://dev.to/gasik1417/-hmk</link>
      <guid>https://dev.to/gasik1417/-hmk</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/gasik1417/api-versioning-is-not-application-versioning-4c9p" class="crayons-story__hidden-navigation-link"&gt;API Versioning is Not Application Versioning&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/gasik1417" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F2979058%2F0c062e03-adcf-4e45-b54a-cbacadaf336b.png" alt="gasik1417 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/gasik1417" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Larry Gasik
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Larry Gasik
                
              
              &lt;div id="story-author-preview-content-4362907" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/gasik1417" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F2979058%2F0c062e03-adcf-4e45-b54a-cbacadaf336b.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Larry Gasik&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/gasik1417/api-versioning-is-not-application-versioning-4c9p" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 10&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/gasik1417/api-versioning-is-not-application-versioning-4c9p" id="article-link-4362907"&gt;
          API Versioning is Not Application Versioning
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/api"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;api&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/versioning"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;versioning&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/architecture"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;architecture&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/http"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;http&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/gasik1417/api-versioning-is-not-application-versioning-4c9p" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt;&amp;nbsp;reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/gasik1417/api-versioning-is-not-application-versioning-4c9p#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            8 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>API Versioning is Not Application Versioning</title>
      <dc:creator>Larry Gasik</dc:creator>
      <pubDate>Mon, 10 Aug 2026 18:48:52 +0000</pubDate>
      <link>https://dev.to/gasik1417/api-versioning-is-not-application-versioning-4c9p</link>
      <guid>https://dev.to/gasik1417/api-versioning-is-not-application-versioning-4c9p</guid>
      <description>&lt;h2&gt;
  
  
  Your API is a promise to your clients. Try not to break it.
&lt;/h2&gt;

&lt;p&gt;API versioning sounds like one of those topics that should be pretty simple.&lt;/p&gt;

&lt;p&gt;Put &lt;code&gt;/v1&lt;/code&gt; in the URL. Eventually make &lt;code&gt;/v2&lt;/code&gt;. Congratulations, you have versioned an API.&lt;/p&gt;

&lt;p&gt;Except that is not really the important part.&lt;/p&gt;

&lt;p&gt;Before talking about API versions, we need to understand what an API actually is, what a client is depending on when it calls one, and why seemingly harmless changes can cause somebody else's application to start throwing exceptions&lt;/p&gt;

&lt;p&gt;The important part of API versioning is not the version number, it is the contracts involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Explain What an API is first.
&lt;/h2&gt;

&lt;p&gt;At its simplest, &lt;a href="https://www.oreilly.com/library/view/api-management-an/9798868800542/" rel="noopener noreferrer"&gt;an API is an interface that allows one piece of software to interact with another piece of software&lt;/a&gt;. Think of it as the doorway into the capabilities provided by an application.&lt;/p&gt;

&lt;p&gt;Imagine I build an application for managing a hockey team.&lt;/p&gt;

&lt;p&gt;Maybe it keeps track of players, games, standings, goals, penalties, and which defenseman said they'd show but never really did, and now everyone's confused about why we're a player short!&lt;/p&gt;

&lt;p&gt;Another application could interact with it through a RESTful API with something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /players/88
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And receive:&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;88&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;"Patrick Kane"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RW"&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;More than likely, there's some sort of front end application that is making the calls to this API. There might be a mobile application. But there might be another backend service calling it.&lt;/p&gt;

&lt;p&gt;There might be absolutely no other way to interact with the hockey player application.&lt;/p&gt;

&lt;p&gt;Sometimes developers think of the API as something bolted onto an application. In many modern systems, the API effectively is the application's external interface. The business logic, database, frameworks, and infrastructure sit behind it.&lt;/p&gt;

&lt;p&gt;The API defines how other software gets access to those capabilities.&lt;/p&gt;

&lt;p&gt;That interface creates a contract between the application providing the API and the applications consuming it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your API Is a Contract
&lt;/h2&gt;

&lt;p&gt;When we say API contract, we are talking about much more than a URL.&lt;/p&gt;

&lt;p&gt;Let's go back to the previous request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /players/88
Authorization: Bearer someToken
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this response:&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;88&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;"Patrick Kane"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"position"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RW"&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;There are already quite a few pieces of the contract hiding in this tiny example.&lt;/p&gt;

&lt;p&gt;The client knows that the resource exists at &lt;code&gt;/players/88&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It knows that it should use &lt;code&gt;GET&lt;/code&gt;, it knows authentication is required. It knows the response will be in JSON.&lt;/p&gt;

&lt;p&gt;The response body will have an &lt;code&gt;id&lt;/code&gt;. It knows there will be a &lt;code&gt;name&lt;/code&gt;. It knows there will be a &lt;code&gt;position&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It probably knows what HTTP status codes to expect when something goes wrong.&lt;/p&gt;

&lt;p&gt;Those expectations are the contract.&lt;/p&gt;

&lt;p&gt;A typical API contract includes things like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The URI of the resource&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The HTTP methods that are supported&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Request parameters&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Request body formats&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Response formats&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data types&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;HTTP status codes and error behavior&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Authentication and authorization requirements&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The meaning of the data being exchanged&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The implementation behind that contract can be completely different.&lt;/p&gt;

&lt;p&gt;That gives us an important architectural boundary.&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%2Fq4nlz2fuqfhog2mceqcl.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%2Fq4nlz2fuqfhog2mceqcl.png" alt="Architectural Boundaries" width="445" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The client depends on the contract, nothing else.&lt;/p&gt;

&lt;p&gt;The client doesn't care about the implementation. Those details are abstracted away.&lt;/p&gt;

&lt;p&gt;You could rewrite the entire application. You could change frameworks. You could change databases. You could change your data source from a database to a text file.&lt;/p&gt;

&lt;p&gt;You could replace one gigantic service with several smaller services.&lt;/p&gt;

&lt;p&gt;You could discover that the original developer made some truly fascinating architectural decisions at 2:00 AM and replace half the application.&lt;/p&gt;

&lt;p&gt;If the contract stays the same, your clients may never know, and shouldn't care.&lt;/p&gt;

&lt;p&gt;That is a feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Yes, Your Coworker's Application Is Still a Client
&lt;/h2&gt;

&lt;p&gt;Words like service and module are so generic that they eventually mean nothing. The term client in this case can sometimes causes confusion, especially in this context.&lt;/p&gt;

&lt;p&gt;People hear "client" and immediately think about some external company paying to use an API.&lt;/p&gt;

&lt;p&gt;That is certainly a client. But it is not the only kind. If another application consumes your API, that application is your client.&lt;/p&gt;

&lt;p&gt;It does not matter if both applications belong to the same company. It does not matter if both engineering teams report to the same manager. It does not matter if the developer consuming your API sits six feet away from you. Think of it more in the HTTP Lifecycle style of client/server architecture.&lt;/p&gt;

&lt;p&gt;Suppose we have a Hockey API:&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%2Fy8p6pbb09srcw1xiam7d.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%2Fy8p6pbb09srcw1xiam7d.png" alt="Three Clients, One API" width="408" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All three systems are clients.&lt;/p&gt;

&lt;p&gt;They may all operate in the same building, same server, built by the same team, but breaking the API still ruins somebody else's night.&lt;/p&gt;

&lt;p&gt;Internal APIs deserve contract management too.&lt;/p&gt;

&lt;p&gt;It is possible that ignoring contracts on internal APIs can be particularly dangerous because teams sometimes assume they have more freedom to change things and do what they need to get the job done.&lt;/p&gt;

&lt;p&gt;"We own both applications."&lt;/p&gt;

&lt;p&gt;Great.&lt;/p&gt;

&lt;p&gt;Does that mean both applications are always deployed simultaneously? Does the same team maintain both of them? Do you know every application currently calling the API? Will you still know that three years from now?&lt;/p&gt;

&lt;p&gt;Probably not. It sounds like tight coupling to me.&lt;/p&gt;

&lt;p&gt;Internal does not mean dependency free.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tiny Change That Breaks Everything
&lt;/h2&gt;

&lt;p&gt;Now imagine somebody decides this field name is not very good because naming things is hard:&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;"Patrick Kane"&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;They clean it up:&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;"playerName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Patrick Kane"&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;That seems harmless.&lt;/p&gt;

&lt;p&gt;Maybe &lt;code&gt;playerName&lt;/code&gt; really is a better property name.&lt;/p&gt;

&lt;p&gt;The change passes all the unit tests. The API deploys successfully. Everything looks fantastic. WORKS ON MY MACHINE!&lt;/p&gt;

&lt;p&gt;Meanwhile, somewhere else:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;playerName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whoops!&lt;/p&gt;

&lt;p&gt;From the perspective of the API developer, one property was renamed.&lt;/p&gt;

&lt;p&gt;From the perspective of the client, the contract was broken.&lt;/p&gt;

&lt;p&gt;That distinction is the entire reason API version management exists.&lt;/p&gt;

&lt;p&gt;Software changes all the time. An API should not receive a new version every time the software changes.&lt;/p&gt;

&lt;p&gt;A bug fix does not require API V2. Refactoring some business logic does not require API V2. Updating your framework does not require API V2. Changing your database does not require API V2.&lt;/p&gt;

&lt;p&gt;The important question is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Did I change the shape of the doorway my clients use? Did I change the contract my clients depend on?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Not Every API Change Needs a New Version
&lt;/h2&gt;

&lt;p&gt;This brings us to backward compatibility.&lt;/p&gt;

&lt;p&gt;Some changes allow existing clients to continue working exactly as they did before. Say we add a new optional query parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /players?position=RW
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Existing clients do not have to use it. Nothing broke. Maybe we add a completely new endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /teams/17/schedule
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, existing clients can happily ignore it. They don't know about it and they don't care. Maybe we add an optional property to a request. Existing clients can continue sending the same request they sent yesterday.&lt;/p&gt;

&lt;p&gt;Those are generally backward compatible changes. Now consider some different changes.&lt;/p&gt;

&lt;p&gt;You rename a response property. You remove a response property. You add a new required request property. You change the structure of a response. You change the meaning of an existing value. You've changed the doorway significantly. Now existing clients may need code changes.&lt;/p&gt;

&lt;p&gt;That is a breaking contract change.&lt;/p&gt;

&lt;p&gt;A useful mental model is this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj491u0c3tfsyrvzg9msr.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%2Fj491u0c3tfsyrvzg9msr.png" alt="Will existing clients continue to work without modification?" width="571" height="671"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is not a perfect universal rule. Software architecture rarely gives us those. It is a very good question to ask before deciding that you need another API version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Versioning Has a Cost Too
&lt;/h2&gt;

&lt;p&gt;There is another trap here.&lt;/p&gt;

&lt;p&gt;Once teams discover versioning, sometimes everything becomes a new version.&lt;/p&gt;

&lt;p&gt;V1, V2, V3, V4....&lt;/p&gt;

&lt;p&gt;At some point you have created the API equivalent of keeping every hockey jersey you have owned since you were twelve because you might need one of them again.&lt;/p&gt;

&lt;p&gt;Every API version has a cost.&lt;/p&gt;

&lt;p&gt;If clients are still using V1 when you release V2, you may need to support both.&lt;/p&gt;

&lt;p&gt;Now bug fixes may need to be tested against multiple versions. Documentation needs to explain multiple versions. Monitoring needs to distinguish between versions. Developers consuming the API need to understand what changed.&lt;/p&gt;

&lt;p&gt;Eventually the client will need to migrate to the new version of the API.&lt;/p&gt;

&lt;p&gt;This is why backward compatibility matters so much. The goal is not to become really good at creating API versions. The goal is to become really good at evolving an API without unnecessarily creating them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Does the Version Go?
&lt;/h2&gt;

&lt;p&gt;Eventually, you will have a legitimate breaking change. When that happens you need some way for identifying versions.&lt;/p&gt;

&lt;p&gt;One common approach is including the version in the URI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/api/v1/players
/api/v2/players
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are other approaches.&lt;/p&gt;

&lt;p&gt;You can communicate the version through request headers, or query parameters. Completely different hostnames can even be used for major redesigns.&lt;/p&gt;

&lt;p&gt;There are plenty of arguments about which approach is best, and that discussion can become an entire post by itself.&lt;/p&gt;

&lt;p&gt;For now, I think there is a more important lesson.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How you version your API is significantly less important than the understanding of why the new API version exists.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you are creating V2 because the implementation changed, you are probably thinking about the wrong version.&lt;/p&gt;

&lt;p&gt;If you are creating V2 because the contract must change in a way that existing clients cannot understand, now we have a reason to talk about API versioning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do Not Just Kill V1
&lt;/h2&gt;

&lt;p&gt;Eventually V1 needs to go away. Don't just kill it, and allow your clients start getting errors in production. They didn't do anything wrong. Retiring a contract has to be a coordinated change when your client depends on said contract.&lt;/p&gt;

&lt;p&gt;A reasonable lifecycle might look something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Release V2&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run V1 and V2 Simultaneously&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Notify V1 clients&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Publish migration documentation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Provide a migration window&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deprecate V1&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Retire V1&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Clients should know what is happening and when.&lt;/p&gt;

&lt;p&gt;That can include release notes, documentation updates, developer portals, emails, and direct communication with teams that you know consume the API. The management around this can be its own topic as well, we're just talking about contracts.&lt;/p&gt;

&lt;p&gt;You can also communicate deprecation through HTTP itself.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Deprecation&lt;/code&gt; &lt;a href="https://http.dev/deprecation" rel="noopener noreferrer"&gt;response header&lt;/a&gt; is now standardized for telling clients that a resource has been deprecated or will be deprecated.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Sunset&lt;/code&gt; &lt;a href="https://http.dev/sunset" rel="noopener noreferrer"&gt;response header&lt;/a&gt; can communicate when a URI is expected to stop responding.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Deprecation: @1798761600
Sunset: Wed, 30 Jun 2027 23:59:59 GMT
Link: &amp;lt;https://api.example.com/docs/migration&amp;gt;; rel="deprecation"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A human reading an email can understand that V1 is going away.&lt;/p&gt;

&lt;p&gt;A client, monitoring system, or development tool can potentially understand these headers too.&lt;/p&gt;

&lt;p&gt;Retirement should not be a surprise. Give clients time to move. Run versions together when appropriate. Know who is using the version you are trying to retire. Then actually retire it.&lt;/p&gt;

&lt;p&gt;Keeping twelve versions alive forever is not API lifecycle management. It is API archaeology. This is technical debt, and every version you keep around gives you another thing to build, test, document, monitor, and eventually fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Rule I Want You to Remember
&lt;/h2&gt;

&lt;p&gt;There are a lot of details involved in API versioning causing us to say "it depends". Have the conversations. We can argue about URLs versus headers, or version numbering strategies.&lt;/p&gt;

&lt;p&gt;We can build API gateways, developer portals, governance processes, and migration policies.&lt;/p&gt;

&lt;p&gt;Those things help and are important.&lt;/p&gt;

&lt;p&gt;If you can take one idea, one key concept, one statement away that explains it all from this post -&lt;strong&gt;Your API version is not the version of your application. It is the version of the promise you made to your clients.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your implementation belongs to you, and is abstracted away from the client. Your contract is shared. Treat it accordingly.&lt;/p&gt;

&lt;p&gt;And before you rename &lt;code&gt;name&lt;/code&gt; to &lt;code&gt;playerName&lt;/code&gt; five minutes before deployment because "it looks cleaner," maybe check who is using it first.&lt;/p&gt;

</description>
      <category>api</category>
      <category>versioning</category>
      <category>architecture</category>
      <category>http</category>
    </item>
    <item>
      <title>Quarterly Check In - 2026, Q1</title>
      <dc:creator>Larry Gasik</dc:creator>
      <pubDate>Sat, 11 Apr 2026 16:30:23 +0000</pubDate>
      <link>https://dev.to/gasik1417/quarterly-check-in-2026-q1-m9j</link>
      <guid>https://dev.to/gasik1417/quarterly-check-in-2026-q1-m9j</guid>
      <description>&lt;p&gt;Over the last quarter, I have found it more difficult to maintain the level of focus and consistency I expect from myself. There is no single cause. It is a combination of seasonal factors, increased responsibilities, and the natural accumulation of competing priorities.&lt;/p&gt;

&lt;p&gt;Winter tends to have a noticeable impact. Shorter days and reduced sunlight affect energy levels more than I would like to admit. At the same time, personal responsibilities increase. Family, home ownership, pets, and the day to day logistics of life all require attention. Add in the residual fatigue from the holiday season and the normal pressures of a demanding role, and it becomes clear how quickly things can compound.&lt;/p&gt;

&lt;p&gt;At work, the expectations are high, and rightly so. There is a constant push to deliver, guide teams, and support multiple initiatives. When you are balancing several areas at once, it can feel like managing a set of spinning plates, with new ones being added when you may be already struggling to keep what you have going. That pressure can make it harder to invest in longer term personal growth, even when it remains a priority.&lt;/p&gt;

&lt;p&gt;That does not mean I have ignored personal development. There has been a meaningful amount of learning, a number of strong technical discussions, and time spent helping others one on one. I do feel it has been unfocused. Still, the last few weeks have provided a useful shift in perspective.&lt;/p&gt;

&lt;p&gt;A recent conversation with a colleague stood out. They came across some of my writing, both internal and external, and shared that they found it valuable and were even thinking about starting to write themselves. Whether they actually do is not the point. The fact that it made them consider it means something. It means I created a spark.&lt;/p&gt;

&lt;p&gt;That was a reminder that writing is not just documentation or self expression. It has real value. It helps others think, learn, and take action. That realization gave me renewed energy to continue building and sharing.&lt;/p&gt;

&lt;p&gt;One of the many reasons I started writing in the first place was to help people I have worked with. There is something valuable about being approachable. If someone already knows you, it is easier for them to reach out, ask questions, and engage, rather than trying to connect with an expert stranger who wrote the book on whatever topic.&lt;/p&gt;

&lt;p&gt;Another observation is that much of my recent enjoyment, and even many of my writing ideas, have come from hands on development work. That is not a bad thing, but it does highlight the need for balance. A lot of what I read and write about is driven by problems I am actively trying to solve.&lt;/p&gt;

&lt;p&gt;Moving forward, I want to be more intentional about maintaining that balance. Coding sharpens execution. Reading builds perspective. Writing sharpens thinking. All three matter.&lt;/p&gt;

&lt;p&gt;One immediate goal is to complete the unit testing series I started. It grew beyond its original scope, which forced me to split it into multiple parts. Finishing it is important, not just to close the loop, but to reinforce consistency. It is also a topic worth finishing properly.&lt;/p&gt;

&lt;p&gt;I have also been thinking about making my list of blog post ideas public. There is value in sharing the process, not just the finished output.&lt;/p&gt;

&lt;p&gt;One pattern I continue to notice is my tendency to favor large, uninterrupted blocks of work. In theory, that approach maximizes efficiency and minimizes context switching. In practice, it is not always realistic.&lt;/p&gt;

&lt;p&gt;Progress does not always come from large efforts. More often, it comes from small, consistent steps. Accepting that has been an important adjustment. Instead of waiting for the perfect block of time, I need to make better use of the time that is available and stay focused.&lt;/p&gt;

&lt;p&gt;That includes being more flexible with where and how I work. I already rotate between environments such as home, libraries, and other quiet spaces. Being more intentional about that can create better opportunities for focused work.&lt;/p&gt;

&lt;p&gt;One thing that has helped is taking deliberate time to clear my head. Even small breaks can reset perspective and improve clarity. That cannot be a once in a while activity. It needs to be consistent. I used to be disciplined about this, and recently I have been in a constant state of pushing forward without pause.&lt;/p&gt;

&lt;p&gt;Without that separation, everything starts to blend together. Work, personal responsibilities, and long term goals compete for the same mental space. It becomes easy to minimize progress and move immediately to the next problem without acknowledging what has been accomplished. Creating separation, recognizing progress, and allowing space to reset are necessary to stay effective.&lt;/p&gt;

&lt;p&gt;I would not describe the last quarter as unproductive, but it has been uneven. The goal now is not to overcorrect, but to reintroduce consistency.&lt;/p&gt;

&lt;p&gt;There is a lot in motion, both professionally and personally. That is unlikely to change. What can change is how I structure my time and energy within it.&lt;/p&gt;

&lt;p&gt;This may read like a stream of thoughts, but that is intentional. I want to acknowledge how demanding this type of career can be, and how important it is to periodically step back and recalibrate. Taking time to reflect, even informally like this, helps ensure that I am still aligned with what I expect from myself and what I actually enjoy doing. If you lose site of what brings you joy, you will burn out.&lt;/p&gt;

</description>
      <category>mentalhealth</category>
    </item>
    <item>
      <title>Stop Guessing - Automated Unit Tests Tell You How Your Code Behaves</title>
      <dc:creator>Larry Gasik</dc:creator>
      <pubDate>Wed, 18 Mar 2026 17:31:49 +0000</pubDate>
      <link>https://dev.to/gasik1417/stop-guessing-automated-unit-tests-tell-you-how-your-code-behaves-409m</link>
      <guid>https://dev.to/gasik1417/stop-guessing-automated-unit-tests-tell-you-how-your-code-behaves-409m</guid>
      <description>&lt;p&gt;Automated Unit Tests, or AUT, are a concept that most developers do not initially see as beneficial. When I was introduced to AUT, my reaction was was, “I’m going to write buggy code, to test my buggy code.” It takes takes time to see the true benefit of AUT. As time has gone on, I've become a huge proponent for AUT.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The true purpose of AUT is to allow the developer to be sure the code behaves as expected.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Burn that bold text into your head because it will be the theme of everything here.&lt;/p&gt;

&lt;p&gt;On the surface, it sounds like I just said the same thing about buggy code testing buggy code. But that is not really what is happening. I am able to test what happens inside my code. I am able to go through different scenarios in milliseconds. I am able to verify success cases, failure cases, edge cases, and exception paths without waiting on a database, a file system, another service, or human interaction. That is where the value starts to show up.&lt;/p&gt;

&lt;p&gt;A few years ago, a friend reached out to me saying that he needed to write AUT for his coding assessment, but he did not fully understand what the true purpose of AUT is. “Why do I need to write code to show that 2 == 2?” I’m a huge proponent of AUT, and my friend did not see the value. We discussed back and forth some different challenges that come with testing. Some of my questions were:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;How do you test your classes based on what is returned from a third party service? What if that service is down?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How do you simulate exceptions?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How do you test interacting with the System namespace?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When do you test?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What if you are maintaining code that you did not write?&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I got some rather lame answers, like, “It doesn’t make sense to test for a service being down, what are the chances it is going to be down?” or “Why test exceptions? I’d just write a catch for it so it doesn’t get thrown to the end user.” If you have ever worked in a distributed environment, you know systems are unavailable from time to time and it is out of your control. Networks fail. DNS changes and they aren't communicated. Tokens expire or APIs throttle. Databases go offline or move. File shares disappear. Cats and dogs living together!!! Somebody rotates a secret and forgets to tell you. I am in awe that it works at all sometimes. But, it was clear that an example was needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Automated Unit Tests?
&lt;/h2&gt;

&lt;p&gt;You do not test a bridge by driving a single car over it right down the middle on a clear, calm day. You do extreme things. You add load. You check wind conditions. You make sure the supports are deep enough. You make sure parts do not shift when the weather changes, especially where there are freezing temperatures. You verify those little reflectors don't pop out of the road when hit by a truck. You look for the weak points before the public uses the bridge&lt;/p&gt;

&lt;p&gt;That is what unit testing is doing for your code. You are not proving your code works in one happy path under perfect conditions. You are deliberately looking for the places it can bend, crack, or do something you did not intend.&lt;/p&gt;

&lt;p&gt;What I’m highlighting are edge cases. Edge cases are the conditions that sit at the extremes of what your code is supposed to handle. They are the places most likely to show breakdowns, different behavior, and exceptions in your code. You are simulating stress on your solution, much like you were simulating stress on your bridge.&lt;/p&gt;

&lt;p&gt;A unit test is meant to exercise a small piece of code, avoid external infrastructure, and run fast enough that developers can execute it frequently. These unit tests shouldn't depend on databases, file systems, or network resources. &lt;a href="https://martinfowler.com/bliki/UnitTest.html" rel="noopener noreferrer"&gt;Fowler&lt;/a&gt; similarly describes unit tests as small in scope and fast enough to run constantly while coding.&lt;/p&gt;

&lt;p&gt;These tests should be automated, quick, repeatable, and consistent. You are testing the smallest practical path in your codebase. It may be an orchestrating method. It could be a validation rule. It may be a helper method. The point is that you can go through a single path and hit the cases you need to hit.&lt;/p&gt;

&lt;p&gt;It also has a great side effect. Writing unit testable code tends to push you toward better design. If a class is miserable to test, that is often a design smell. Maybe it has too many responsibilities. Maybe it reaches into too many dependencies. Maybe it hides logic behind framework calls or static state. Testing has a way of shining a light on that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What good unit tests look like
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Automated
&lt;/h3&gt;

&lt;p&gt;Automated Unit Tests are automated - it is right there in the name. They require no manual intervention, no setup gymnastics, no babysitting, and no clicking around a UI. Kick off your tests and get a result.&lt;/p&gt;

&lt;p&gt;That matters because the real value is not just writing the test once. The real value is rerunning it every time you make a change. A test that needs a human to prepare the environment is already losing its value.&lt;/p&gt;

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

&lt;p&gt;A good unit test should execute in milliseconds. There's no network traffic, no real database work, no disk operations, and no waiting on external systems. I can have no access to the VPN, no LAN, no internet access and still execute my tests. Good unit tests are fast, isolated, and repeatable, and Fowler makes the same case for keeping them small and fast enough to run constantly during development.&lt;/p&gt;

&lt;p&gt;That speed changes developer behavior. If your tests run in milliseconds, you will actually use them. If they take twenty minutes, your team will start asking questions about whether you really need to run them. That is where quality and value starts slipping.&lt;/p&gt;

&lt;h3&gt;
  
  
  Repeatable
&lt;/h3&gt;

&lt;p&gt;Because unit tests do not interact with unstable outside systems, they become repeatable. Data does not need to be manually configured. A network does not need to be available. The same test can run over and over and produce the same result.&lt;/p&gt;

&lt;p&gt;That is a massive benefit during refactoring. When the result changes, you know it is because something changed in the code or the test, not because the planets aren't in line or a shared environment had a bad morning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consistent
&lt;/h3&gt;

&lt;p&gt;Consistency is what separates a useful test suite from a noisy one. A flaky test that passes on one run and fails on the next without any meaningful change is not a safety net. It is background noise. Non deterministic tests become effectively useless because teams stop trusting failures once they become unreliable.&lt;/p&gt;

&lt;p&gt;That is why isolating system behavior matters so much. If your code depends on the current date, abstract the clock. If it depends on file access, wrap the file system. If it depends on an external service, introduce an interface. Then your tests can simulate exactly what you need, and they can do it the same way every time.&lt;/p&gt;

&lt;p&gt;This allows red flags to be raised immediately when a test fails. I can't tell you the number of times when teams write poor tests, they fail, and they just continue on because it is expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we use Automated Testing
&lt;/h2&gt;

&lt;p&gt;Unit tests make sure the developer understands the behavior of the code. In a good codebase, the business rules are not just buried in business logic. They are also reflected in tests that sit side by side the code and explain what is expected to happen.&lt;/p&gt;

&lt;p&gt;The first time I really saw value in an Automated Unit Test was when I was writing validation logic. A client would pass in details to the back end that needed to be verified based on existing information in the database. If the submission was valid, process the update. If not, return an error and do not update the database.&lt;/p&gt;

&lt;p&gt;I wrote the code and it worked. Then I went back and refactored the conditionals. In the process, I introduced a bug that always updated the database, even when the request was invalid. Had I had unit tests verifying that invalid requests never call the update method, I would have caught it immediately.&lt;/p&gt;

&lt;p&gt;That is a big part of the real value. They are there to catch the moment you accidentally broke behavior that used to behave as expected. Regression protection is one of the major benefits of unit tests, and will help you when you come back to a module, or someone new is tinkering around.&lt;/p&gt;

&lt;p&gt;They also serve as living documentation. A well named test tells the next developer, including future you, what the code is supposed to do. Sometimes reading a good test is faster than tracing through production code. If you ever get the chance, check out the names of some of the methods for my unit tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  When do we execute our tests?
&lt;/h2&gt;

&lt;p&gt;Run your unit tests all the time. The sooner you get feedback, the sooner you can correct the problem. Unit tests should be part of your normal workflow, not a special event. One day, I'm going to write about &lt;a href="https://www.jetbrains.com/help/dotcover/Continuous_Testing.html" rel="noopener noreferrer"&gt;continuous testing in dotCover&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  During development
&lt;/h3&gt;

&lt;p&gt;As you build a feature, run your tests. Fowler’s guidance on unit tests is very direct here. Fast tests are valuable because they can be run constantly while programming, often after every meaningful change.&lt;/p&gt;

&lt;p&gt;The faster the feedback, the easier it is to locate the defect. If I break something and find out thirty seconds later, I know roughly where to look because it is fresh in my mind. I'm not going to remember what I did two weeks ago, or even yesterday.&lt;/p&gt;

&lt;h3&gt;
  
  
  During refactoring
&lt;/h3&gt;

&lt;p&gt;If you are making changes to the codebase, you need a quick way to verify that you did not break behavior. That is where a good test suite is valuable. And don't act like you're going back to write unit tests for large chunks of code after it is in production.&lt;/p&gt;

&lt;p&gt;Refactoring without tests is like a surgeon without monitors to vitals. They may make changes in the process, but they have no idea if they broke something in the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  In Continuous Integration
&lt;/h3&gt;

&lt;p&gt;The whole point of Continuous Integration is fast feedback. When code is pushed, you want an automated compilation, and a test run telling you whether the application still behaves as expected. Running a broader commit suite as part of CI, commonly including all unit tests, because the speed and scope make them ideal for that layer of feedback. Keep in mind, these are not integration tests. Those are slower, which serve a different purpose of what you're trying to do here Because unit tests are cheap and fast, they belong in CI. They catch regressions while the change is still fresh in the engineer’s mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Misconceptions about Unit Tests
&lt;/h2&gt;

&lt;p&gt;I grow frustrated when people get the wrong idea of unit tests. Unit tests are a tool, and not a silver bullet. I've found myself fighting some of the same battles over and over.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Unit tests result in bug free code
&lt;/h3&gt;

&lt;p&gt;You're still going to have bugs in your code. Unit tests reduce risk. They increase confidence. They catch regressions. They absolutely do not guarantee bug free software. A unit test only tells you whether a specific behavior matched a specific expectation under a specific condition. It is a big reason why you need many tests.&lt;/p&gt;

&lt;p&gt;We still need integration tests, system tests, exploratory testing, and plain old human judgment.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Unit tests are difficult to maintain
&lt;/h3&gt;

&lt;p&gt;Bad unit tests are difficult to maintain. Good unit tests are usually a reflection of good design.&lt;/p&gt;

&lt;p&gt;When production code follows sane design principles, especially explicit dependencies and separation of concerns, the tests are easier to write and easier to keep. Microsoft’s ASP.NET Core testing guidance leans on dependency injection and explicit dependencies specifically because those patterns make code testable.&lt;/p&gt;

&lt;p&gt;Honestly, if you follow the SOLID Principles, AUT is really easy.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. AUT is the same thing as Test Driven Development
&lt;/h3&gt;

&lt;p&gt;This statement can make me red in the face. TDD is a development practice. Unit testing is a testing technique. They overlap, but they are not the same thing. Fowler has also written about how people often confuse self testing code with TDD, even though TDD is only one path to getting there.&lt;/p&gt;

&lt;p&gt;You can write good unit tests without following a strict red, green, refactor cycle. From my experience, There aren't many shops following TDD as designed. You can tell when someone is doing TDD because their code is a bit different.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. More tests means more quality
&lt;/h3&gt;

&lt;p&gt;Garbage tests are a waste of time. Garbage tests are often a result of a misunderstanding of what unit tests are supposed to do, or a need to fill a vanity metric.&lt;/p&gt;

&lt;p&gt;A hundred fragile, shallow, badly named tests do not make a codebase healthy. They make it noisy. Quality comes from meaningful tests that verify behavior people actually care about.&lt;/p&gt;

&lt;p&gt;I fully support a mandate around Unit Test Code Coverage. It can be useful as a signal, but it is not the goal. You can hit a coverage number and still miss the real business rules, but you can't have a good test suite, and a low coverage percentage.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Unit tests eliminate the need for manual testing
&lt;/h3&gt;

&lt;p&gt;They do not.&lt;/p&gt;

&lt;p&gt;Manual testing, especially exploratory testing, still matters. Unit tests are excellent at fast, repeatable checks of expected behavior. They are not great at discovering confusing workflows, odd usability problems, or the kinds of real world chaos people create the minute your software lands in front of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common challenges
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Writing testable code
&lt;/h3&gt;

&lt;p&gt;This is usually the first real hurdle. If your code reaches directly into the current time, the file system, static state, configuration, HTTP calls, and database access all in one method, testing it is going to hurt.&lt;/p&gt;

&lt;p&gt;That pain is often telling you something useful about the design. Take a step back and redesign your code.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Legacy code
&lt;/h3&gt;

&lt;p&gt;Legacy code often means tightly coupled code with very few seams. That makes introducing tests harder, but also more valuable.&lt;/p&gt;

&lt;p&gt;You may not be able to drop in perfect unit coverage on day one. Sometimes the first step is characterization testing, writing tests around current behavior so you can make changes without guessing.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Mocking and dependency injection
&lt;/h3&gt;

&lt;p&gt;There is a learning curve here. Developers who are new to interfaces, dependency injection, and test doubles often feel like this is extra ceremony with no benefit.&lt;/p&gt;

&lt;p&gt;In practice, these design patterns let you replace unstable collaborators with predictable ones. That is exactly why you need to isolate your unit tests. Dependency injection allows swapping implementations for testing, including mocked services in controller tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Balance
&lt;/h3&gt;

&lt;p&gt;How many tests do we write? How many assertions in one method are too many? What do we verify?&lt;/p&gt;

&lt;p&gt;Those are real questions, and there is no magic number. I would rather have one sharp test that verifies a meaningful rule than five vague tests that mostly restate the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Continuous Integration
&lt;/h3&gt;

&lt;p&gt;Not every shop is doing Continuous Integration, and it can be hard to bring to the team if your build process is convoluted. It can be hard to introduce testing into an existing CI process, especially if teams are already used to slower, unstable test suites. That said, this is exactly where unit tests shine, because they are the cheapest automated feedback you can add to the pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Skill gap
&lt;/h3&gt;

&lt;p&gt;Effective unit testing requires shared understanding. The team needs to agree on what a unit test is, what belongs in one, what does not, and what “good” looks like.Otherwise, one person writes isolated tests, another person writes mini integration tests and calls them unit tests, and then everybody argues about testing while the code rots.&lt;/p&gt;

&lt;p&gt;Getting everyone on the same page is the biggest challenge.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Knowing what to test
&lt;/h3&gt;

&lt;p&gt;What if you're coding and you don't have the true requirements defined yet? Sometimes the logic is unclear. Sometimes the code is unclear. Sometimes product doesn't even know what is supposed to happen.&lt;/p&gt;

&lt;p&gt;Another hidden benefit of testing brought to light.... writing tests forces clarity and engineers have to ask questions. “What is this thing actually supposed to do?” Don't get answers, and you're in a bad shape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hands On is the way to be
&lt;/h2&gt;

&lt;p&gt;Look, I’ve been writing unit tests for over 15 years. I’ve helped others start writing unit tests. I’ve struggled, and I’ve seen others struggle to write them too. It isn’t about 2 == 2. It’s about giving the developer confidence that the code behaves as expected. You’re testing happy paths, failures, edge cases, and exception handling in milliseconds. You’re building confidence in your codebase.&lt;/p&gt;

&lt;p&gt;Focus on the most concerning areas first. You’re going to have to isolate dependencies, and if you’ve thrown things together quickly, it will take time to get it right. Go incrementally, and as you do, treat your tests as documentation for what you’ve built.&lt;/p&gt;

&lt;p&gt;In an upcoming post, I’m going to walk through some code samples and patterns that I’ve used. I’ll cover testing exceptions, code coverage, mocking, and all the good stuff. Hands-on is the way to go.&lt;/p&gt;

</description>
      <category>unittest</category>
      <category>ci</category>
      <category>testing</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
