<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ versioning - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ versioning - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 22 May 2026 17:40:19 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/versioning/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Handle Breaking Changes for API and Event Schemas ]]>
                </title>
                <description>
                    <![CDATA[ Several years ago while designing APIs for an ecommerce company, I discovered the importance of API versioning. So I wrote about it in a freeCodeCamp article entitled How to Version a REST API.  Now I find myself designing event schemas for sending m... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-handle-breaking-changes/</link>
                <guid isPermaLink="false">66bccb69ec0f48126680c59a</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Microservices ]]>
                    </category>
                
                    <category>
                        <![CDATA[ schema ]]>
                    </category>
                
                    <category>
                        <![CDATA[ versioning ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tim Kleier ]]>
                </dc:creator>
                <pubDate>Thu, 19 Oct 2023 14:18:25 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/10/7115374283_30d07f11c3_c-2.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Several years ago while designing APIs for an ecommerce company, I discovered the importance of API versioning. So I wrote about it in a <a target="_blank" href="https://www.freecodecamp.org/news">freeCodeCamp</a> article entitled <a target="_blank" href="https://www.freecodecamp.org/news/how-to-version-a-rest-api/">How to Version a REST API</a>. </p>
<p>Now I find myself designing event schemas for sending messages across a distributed system. It's a very similar problem with similar pain points and solutions. Adhering to data contracts is critical to ensure we don't frustrate event subscribers or bring down systems.</p>
<p>Versioning APIs is translatable to versioning event schemas, but if you can effectively evolve schemas, you don't actually need versioning. Effective evolution of schemas comes down to avoiding breaking changes. </p>
<p>Though I covered that briefly in the article above, here I want to thoroughly address breaking changes and propose more solutions to avoiding them.</p>
<h2 id="heading-what-are-breaking-changes">What are Breaking Changes?</h2>
<p>Essentially, a breaking change to a schema (in an API or event context) is anything that requires a consumer to make an update on their end. It's a change that forces change. Schemas will evolve, but once a schema is in use in production, you have to be very careful not to break the data contract. </p>
<p>Removing an event format or changing an event's basic structure constitutes a breaking change. But the nuts and bolts are at the attribute (the field or property) level. </p>
<h3 id="heading-structural-breaking-changes">Structural breaking changes</h3>
<p>Here's a list of structural breaking changes for schema attributes:</p>
<ul>
<li><strong>Renaming Attributes</strong> – Changes to an attribute's name, even if it's just changing the case (for example, from camelCase to TitleCase), is a breaking change.</li>
<li><strong>Removing Attributes</strong> – Taking an attribute out of a schema.</li>
<li><strong>Data Types Changes</strong> – Changing data types, even if the change seems compatible.</li>
<li><strong>Making Attributes Required</strong> – Anytime you mark an attribute (even a new one) as required when it wasn't before, it's a breaking change.</li>
</ul>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Type</td><td>Example</td></tr>
</thead>
<tbody>
<tr>
<td>Renaming Attributes</td><td><code>name</code> to <code>firstName</code></td></tr>
<tr>
<td>Removing Attributes</td><td>Introducing <code>firstName</code> but removing <code>name</code></td></tr>
<tr>
<td>Data Type Changes</td><td>Changing <code>productSKU</code> from <code>integer</code> to <code>string</code></td></tr>
<tr>
<td>Making Attributes Required</td><td>Now requiring a <code>customerID</code></td></tr>
</tbody>
</table>
</div><h3 id="heading-semantic-breaking-changes">Semantic breaking changes</h3>
<p>The other primary category of attribute-level breaking changes has to do with changes in what the data means, or semantic changes. They force consumers to re-interpret the data they're getting. </p>
<p>They are as follows:</p>
<ul>
<li><strong>Format Changes</strong> – Any change to the format of an attribute. </li>
<li><strong>Meaning Changes</strong> – When the declared or implied meaning of data changes.</li>
<li><strong>Stricter Constraints</strong> – When attribute requirements are added or made more restrictive.</li>
</ul>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Type</td><td>Example</td></tr>
</thead>
<tbody>
<tr>
<td>Format Changes</td><td>Date from <code>mm/dd/yyyy</code> to <code>yyyy-mm-dd</code></td></tr>
<tr>
<td>Meaning Changes</td><td>Changing an enum, changing <code>providerCost</code> from dollars to cents</td></tr>
<tr>
<td>Stricter Constraints</td><td>Adding <code>percentage</code> maximum of 100 </td></tr>
</tbody>
</table>
</div><p>It's important to note that what counts as a "breaking change" might be more nuanced. Changing an <code>amount</code> from dollars to cents still forces a change by event subscribers, in interpreting the <em>meaning</em> of the data being sent. Be careful of those, as they are not always obvious.</p>
<h2 id="heading-what-are-non-breaking-changes">What are Non-Breaking Changes?</h2>
<p>We can generally describe non-breaking changes as additive or permissive ones. These are changes that don't require change for consumers. </p>
<p>Here is a list of non-breaking attribute changes: </p>
<ul>
<li><strong>Adding New Attribute</strong> – In all schema contexts, the addition of a new attribute is a non-breaking change, so long as it's not required (for example, for a POST request). </li>
<li><strong>Making Attribute Not Required</strong> – When an attribute was required but newer schema versions do not require it.</li>
<li><strong>Looser Constraints</strong> – Things like more permissive integer ranges (min and max) or allowing for greater decimal precision. Be cautious and communicate with consumers, though, as they may rely on stricter constraints.</li>
</ul>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Type</td><td>Example</td></tr>
</thead>
<tbody>
<tr>
<td>Adding New Attribute</td><td>Adding <code>firstName</code> alongside <code>name</code></td></tr>
<tr>
<td>Making Attribute Not Required</td><td><code>customerID</code> is no longer required</td></tr>
<tr>
<td>Looser Constraints</td><td>Percentage max increased from 100 to 200</td></tr>
</tbody>
</table>
</div><p>Non-breaking changes can often be avoided. But evolving schemas effectively can be challenging and require a lot of thought so as not to break schemas and consumers' trust.</p>
<h2 id="heading-how-to-evolve-schemas">How to Evolve Schemas</h2>
<p>Sadly, the list of breaking changes is longer than the non-breaking ones. But there are some strategies for evolving schemas in a non-breaking way.</p>
<ol>
<li><strong>Domain Knowledge</strong> – Understanding the domain will help ensure you don't end up with poorly named attributes, attributes on the wrong object, or incorrect data types.</li>
<li><strong>Specific Attribute Names</strong> – Rather than changing an attribute's name, data type, or format, introduce a new attribute with a more specific name and correct the data type or format.</li>
<li><strong>Attribute Names With Intent</strong> – Leverage attribute names that reflect their format or intent. For example, consumers might not know whether <code>providerCost</code> would be in dollars or cents, so specify with <code>providerCostInDollars</code> or <code>providerCostInCents</code>. This will also prevent a breaking change if you're having calculation precision issues with dollars and decide to deliver the cost in cents. </li>
<li><strong>Drafted Schemas &amp; Attributes</strong> – Utilize "draft mode" extensively at the schema level, getting feedback on attributes in simulated environments before they are live in production. For schemas that are in use in production, you could introduce a <code>draftedAttributes</code> object and dump experimental (non-production ready) attributes into it. Communicate with consumers that they attributes are being refined – so they should expect breaking changes – and will be moved to the main schema when ready.</li>
<li><strong>Support Existing Attributes</strong> – Leave old attributes in the schema. Don't remove old attributes unless you've been able to coordinate a deprecation/sunsetting strategy with consumers.</li>
<li><strong>Versioning</strong> – If necessary, version your schemas. Although it can become quite difficult to maintain, versioning your schemas is a way to allow for backwards compatibility but move forward with a new schema. You can do high-level versioning (for example, v1 and v2) or more granular semantic versioning (for example, v1.0.1). It's best to version each schema independently, so you don't have to, for example, copy all API v1 endpoints to v2. </li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Breaking changes are a quick way to break trust for any API consumers or event subscribers. I hope that the guidelines above will provide more insight what constitutes a breaking versus non-breaking change, and how to evolve schemas effectively.</p>
<p>If you can't avoid breaking changes, <strong>make sure to coordinate with any and all consumers.</strong> You can actually earn more trust with your consumers if you effectively evolve schemas and communicate breaking changes when they are necessary. </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Release Management for Modern Software Development – How to Manage Dependencies, SemVer, and Build Systems for Beginners ]]>
                </title>
                <description>
                    <![CDATA[ By Nabil Tharwat Releasing modern software might seem daunting and complicated. In this article, I'll expand on the concepts involved in the process, from managing dependencies to building in the cloud. Articles and tutorials usually cover a specific... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/release-management-modern-software-development/</link>
                <guid isPermaLink="false">66d460414bc8f441cb6df813</guid>
                
                    <category>
                        <![CDATA[ dependency management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ deployment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ versioning ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 31 Aug 2021 17:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/08/xavi-cabrera-kn-UmDZQDjM-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Nabil Tharwat</p>
<p>Releasing modern software might seem daunting and complicated. In this article, I'll expand on the concepts involved in the process, from managing dependencies to building in the cloud.</p>
<p>Articles and tutorials usually cover a specific tool and dive right into it before laying down the foundational knowledge. In this article, I'll provide that foundation by introducing the concepts that go into these tools.</p>
<p>The topics I'll cover include dependency management (and what dependencies really are!), build systems, and continuous integration systems with a little bit of icing on the cake. Having this background will help set you up for what's to come.</p>
<h2 id="heading-what-are-libraries">What are Libraries?</h2>
<p>Say you're chipping away dutifully at your tasks. You create a collection of utilities that make your job easier. You're then assigned to a different project in which you need the same utilities, and copy them over. Congratulations, you just created a library and used it across two projects! 😁</p>
<p>Libraries are collections of pre-written code that developers use to optimise tasks. They boost our productivity by abstracting away the boring and repetitive stuff. Numpy, Matplotlib, Lodash, jQuery, and React are all examples of popular, open-source libraries.</p>
<p>You've probably noticed that each of these libraries (or any other library) has a version number. It's usually constructed as a few numeric fields separated by periods: <code>v1.0.0</code> or just <code>1.0.0</code>. These numbers are not random! There are many schemes for defining a version of a product.</p>
<p>Some products use the build number generated by a compiler or CI/CD tool (we'll look into these in a minute). Other products use the date of the build instead of the build number. Others use a build <a target="_blank" href="https://www.freecodecamp.org/news/p/70791fa1-2b5b-4ebc-9927-0e1c06895d4c/%5B%3Chttps://en.wikipedia.org/wiki/Hash_function%3E%5D(%3Chttps://en.wikipedia.org/wiki/Hash_function%3E)">hash</a>.</p>
<p>The most prominent versioning scheme is called <em>Semantic Versioning</em>. It's what most (if not all) code libraries use.</p>
<h2 id="heading-what-is-semantic-versioning-semver">What is Semantic Versioning (Semver)?</h2>
<p>Semantic versioning is a versioning scheme in which you have 3 fields, each separated by a dot. For now, we'll call the first field (on the left side) <em>Major</em>, the one in the middle <em>Minor</em>, and the last one <em>Patch</em>. It looks exactly like this, with some derivations: <code>Major.Minor.Patch</code>.</p>
<p>Per the Semver standard, all fields must increment only. You can't decrement any of them. When a parent version is incremented, all children are reset. So incrementing <em>Major</em> resets <em>Minor</em> and <em>Patch</em> to 0.</p>
<h3 id="heading-the-patch-version">The patch version</h3>
<p>The <em>Patch</em> version is the most frequently changing number. When this number is incremented, it indicates a change that doesn't add new features or break existing functionality. These may be security fixes, performance optimisations, bug fixes, and so on. </p>
<p>Changes to the <em>Patch</em> version are always two-way compatible, as long as parent versions are the same. Code written on <code>v1.0.1</code> will work on <code>v1.0.0</code> and <code>v1.0.2</code>.</p>
<h3 id="heading-the-minor-version">The minor version</h3>
<p>The <em>Minor</em> version is the second most frequently changing number. A change to this number indicates a feature update that doesn't break existing functionality. </p>
<p>Changes to the Minor version are always forward compatible, as long as the Major version is the same. </p>
<p>Code written with <code>v1.1.0</code> <em>will</em> work with <code>v1.2.0</code> but <em>may</em> not work with <code>v1.0.0</code>, as you may be using features added in the more recent version.</p>
<h3 id="heading-the-major-version">The major version</h3>
<p>The <em>Major</em> version is the highest priority and the most "dangerous" field of the three. When this number is incremented, it indicates breaking changes. These are usually API/interface changes and/or entity renaming and removal. </p>
<p>A new Major version is not meant to be compatible with any other Major version, so don't expect <code>v1.0.0</code> to work with <code>v2.0.0</code> or vice versa. Your code <em>may</em> compile after an upgrade, but that's just pure luck. </p>
<p>There are cases in which library authors break underlying logic without affecting the public API you use, so it doesn't break your code. But these are exceptions.</p>
<p>Python 2 and Python 3 are examples of breaking changes. Python 2 print statements don't work on the Python 3 interpreter, and vice versa. Some of it may work, like for loops and other basic structs, but that's about it.</p>
<p>It's recommended that you stay up to date as much as possible with the <em>Patch</em> version. If you need the new features, upgrade your <em>Minor</em> version. A change in <em>Major</em> indicates enormous changes. So be careful when you're upgrading. </p>
<p>There's usually a migration guide with each major release that you should follow. You can read more about Semver in the <a target="_blank" href="https://www.freecodecamp.org/news/p/70791fa1-2b5b-4ebc-9927-0e1c06895d4c/%5B%3Chttps://semver.org/%3E%5D(%3Chttps://semver.org/%3E)">official documentation</a>.</p>
<p>So... how do we install and use external libraries written by other people in the first place?</p>
<h2 id="heading-how-to-manage-your-projects-dependencies">How to Manage Your Project's Dependencies</h2>
<p>In the past, the best that we could do was to actually copy the source code of the libraries we were using into our projects. We applied changes to the library's code, fixed bugs before they were released, and had control over the code. </p>
<p>But this practice, commonly referred to as <em>vendoring</em>, has fallen out of favour for multiple reasons.</p>
<p>If you had applied changes and a new version was released, you had to re-apply all those changes again. It's a manual process that needs to happen every time you update or download a library. It's cumbersome, takes a lot of time, and may break extra functionality that you added.</p>
<p>This quickly gets out of hand when increasing the project's complexity and scale, which leaves us with the better option: <em>Dependency Managers</em>.</p>
<h3 id="heading-what-is-a-dependency-manager">What is a Dependency Manager?</h3>
<p>A dependency is a library or utility that your project needs to work. Simply put, if Program A requires Program B to compile and/or run, Program A is dependent on Program B. A program can depend on multiple other programs. </p>
<p>A dependency manager is a tool that automatically keeps track of a project's dependencies. It allows you to run simple commands in the terminal to install, update, and remove dependencies. NPM, Yarn, Composer, Gradle, and Bundler are all examples of dependency managers.</p>
<p>Don't confuse these with Package Managers, as those are tools that manage system-wide packages. apt-get, yum, Homebrew, and Chocolatey are package managers.</p>
<p>Some package managers can manage system-wide packages and project dependencies. NPM and Yarn are examples of this.</p>
<h3 id="heading-how-does-a-dependency-manager-work">How does a dependency manager work?</h3>
<p>A dependency manager uses two main files: a manifest and a lock file. </p>
<p>The manifest is a list of your project's direct dependencies. It lists the dependencies that you directly specified when installing something. So when you run <code>npm install jsdom</code>, it adds the <code>jsdom</code> package to the list of dependencies in the project's manifest.</p>
<p>But the manifest is not enough. A dependency may have dependencies, and those may have dependencies as well, and so on, forming a <em>dependency graph</em>. A manifest includes only <em>direct</em> dependencies. </p>
<p>Therefore, when you run <code>npm install jsdom</code>, the manifest will only list <code>jsdom</code> despite jsdom having other dependencies of its own. So, how do dependency managers keep track of the whole dependency graph?</p>
<h3 id="heading-what-are-lock-files">What are Lock Files?</h3>
<p>A lock file is a log that lists <em>all</em> the project's dependencies. This includes direct dependencies (listed in the manifest) and the whole dependency graph. It lists every dependency with a specific version, the repository it was fetched from, and other details.</p>
<p>This image shows a comparison between the dependency graph (listed in the lock file) and the direct dependency list (listed in the manifest) of <code>jsdom</code>, a JavaScript implementation of many web standards for testing.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/lock-vs-manifest.png" alt="lock vs manifest.png" width="600" height="400" loading="lazy"></p>
<p>Okay, we know the exact dependency graph, but so what? So everything! We often have multiple developers working on the same project. A dependency manager may install different versions of a library if multiple developers install the project's dependencies using only the manifest.</p>
<p>A lock file locks each dependency in the graph to a specific version, allowing us to have <em>reproducible builds on different machines</em>. This means that every time someone runs <code>npm install</code>, the code <em>is guaranteed to</em> work. This also makes it easier to report bugs by including a lock file in the report.</p>
<p>Lock files also allow dependency managers to reuse cached packages instead of downloading the latest version every time you build your project.</p>
<p>So we've learned what libraries, semantic versioning, and dependency managers are. Now it's time to build our project. </p>
<h2 id="heading-what-are-build-systems">What are Build Systems?</h2>
<p>Every build process is a build system in one way or another. A build system is a set of transformations that transform a source into an artifact. It may be a simple command that starts up a compiler, a script to generate pdf from text files, or even a GUI solution that builds your project and generates a binary.</p>
<p>A build system generally consists of 3 components: </p>
<ul>
<li>Targets</li>
<li>Dependencies</li>
<li>Rules </li>
</ul>
<p>A target is the desired output. If you want a binary called "test.exe", then your target is just that. Dependencies are project dependencies and may include environment utilities like having the C++ compiler installed, npm available, and so on. Rules define how you go from source to target. They may also be the commands used.</p>
<p>A build system may be configured to test your app, generate coverage reports, and lint sources before building as part of its rules. But a build system is manual and local by default. You have to start it up yourself, and it only produces an output on your local machine. </p>
<p>So... what if you want multiple developers to be able to release versions of your app incrementally? This is where CI/CD comes in!</p>
<h2 id="heading-continuous-integration-systems">Continuous Integration Systems</h2>
<p>In short, Continuous Integration (CI) is a paradigm in which you continuously validate changes to a product. A CI system automatically builds and tests every change to avoid problems that may arise when waiting for a release.</p>
<p>Continuous Delivery (CD) is the practice of automating the release process. Major releases are automatically deployed to staging and production, providing an automated release process.</p>
<p>Continuous Deployment (CD) is a step-up from Continuous Delivery. It's the practice of automatically deploying every change if it passes all stages of your production pipeline, without waiting for any explicit approval. This practice emphasizes test automation and user feedback, often leading to multiple software updates a month, week, or even a day!</p>
<p>It's a broad concept that you can read more about in <a target="_blank" href="https://www.atlassian.com/continuous-delivery/principles/continuous-integration-vs-delivery-vs-deployment">this article</a>. For now, we'll refer to the systems that host these practices collectively as Continuous Integration Systems.</p>
<p>A continuous integration system (CI for short) is a build system in the cloud that activates a project's build system on demand and automatically. It's a keystone in the success of agile teams. </p>
<p>CIs consist of three main components:</p>
<ul>
<li>Triggers</li>
<li>Actions</li>
<li>Recipes</li>
</ul>
<p>Triggers are events that the CI listens for to start the build system. These events may be a commit on main branch, a pull request for feature previews, or one of many others. Each platform supports several events.</p>
<p>Actions are commands and scripts that are started upon triggers. You may say: "Build project upon commit on main branch" in the system's language.</p>
<p>Recipes are configurations that specify triggers and actions, environment setup, environment variables, build systems, and system dependencies. They're the system's language. </p>
<blockquote>
<p>Note that you can have multiple build systems on the same CI, each with different targets and rules.</p>
</blockquote>
<p>TravisCI, Jenkins, CircleCI, GitHub Actions, and GitLab CI/CD are examples of CIs we come across every day. The following is an example GitHub Actions recipe to release new versions of a program and send them to GitHub Releases:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span> <span class="hljs-string">//</span> <span class="hljs-string">will</span> <span class="hljs-string">start</span> <span class="hljs-string">the</span> <span class="hljs-string">CI</span> <span class="hljs-string">when</span> <span class="hljs-string">a</span> <span class="hljs-string">push</span> <span class="hljs-string">to</span> <span class="hljs-string">branch</span> <span class="hljs-string">main</span> <span class="hljs-string">is</span> <span class="hljs-string">made</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">release_linux:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span> <span class="hljs-string">//</span> <span class="hljs-string">must</span> <span class="hljs-string">be</span> <span class="hljs-string">run</span> <span class="hljs-string">on</span> <span class="hljs-string">ubuntu@latest</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">check</span> <span class="hljs-string">out</span> <span class="hljs-string">git</span> <span class="hljs-string">repository</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v1</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">install</span> <span class="hljs-string">Node.js,</span> <span class="hljs-string">npm</span> <span class="hljs-string">and</span> <span class="hljs-string">yarn</span> <span class="hljs-string">//</span> <span class="hljs-string">required</span> <span class="hljs-string">env</span> <span class="hljs-string">tools</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v1</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">install</span> <span class="hljs-string">deb</span> <span class="hljs-string">packages</span> <span class="hljs-string">//</span> <span class="hljs-string">required</span> <span class="hljs-string">env</span> <span class="hljs-string">dependencies</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">sudo</span> <span class="hljs-string">apt-get</span> <span class="hljs-string">install</span> <span class="hljs-string">fakeroot</span> <span class="hljs-string">dpkg</span> <span class="hljs-string">rpm</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">build</span> <span class="hljs-string">and</span> <span class="hljs-string">release</span> <span class="hljs-string">app</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">kl13nt/action-electron-forge@master</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-string">//</span> <span class="hljs-string">release</span> <span class="hljs-string">to</span> <span class="hljs-string">github</span> <span class="hljs-string">releases</span> <span class="hljs-string">after</span> <span class="hljs-string">successful</span> <span class="hljs-string">build</span>
          <span class="hljs-attr">release:</span> <span class="hljs-string">${{</span> <span class="hljs-string">startsWith(github.ref,</span> <span class="hljs-string">'refs/tags/v'</span><span class="hljs-string">)</span> <span class="hljs-string">}}</span>
</code></pre>
<p>I've omitted a ton of config stuff in there, but you get the idea. I specified the trigger as a commit on "main" branch and the actions to clone the project's repository, install NodeJS, npm, yarn, and other environment dependencies. </p>
<p>The build stage will run an <a target="_blank" href="https://docs.npmjs.com/cli/v7/commands/npm-run-script/">npm-scripts</a> build system which will lint and test the code before building. The CI will then send the output binaries to the project's GitHub Releases page.</p>
<p>A lock file also comes into play when pushing to a CI as well! If the CI installs different versions of dependencies than the ones you have locally, it may fail. This is why a lock file is as necessary for CIs as it's for developers, so you can rest assured that the code that worked on your machine will work the same way on the CI.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>If you've made it this far, I really hope this was an inspiring (and gentle!) learning experience. You can find more of my content on my <a target="_blank" href="https://iamnabil.netlify.app/">website</a>. Thanks for reading!</p>
<h2 id="heading-further-reading">Further Reading</h2>
<ul>
<li><a target="_blank" href="https://snyk.io/blog/what-is-package-lock-json">What is Package Lock</a></li>
<li><a target="_blank" href="https://missing.csail.mit.edu/2020/metaprogramming">The Missing Semester of Your CS Education - Metaprogramming</a></li>
<li><a target="_blank" href="https://blog.tidelift.com/the-simple-magic-of-package-manifests-and-lockfiles">The Simple Magic of Package Manifests and Lockfiles</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Version a REST API ]]>
                </title>
                <description>
                    <![CDATA[ If you're not very familiar with APIs, you might be wondering...why all the fuss about API versioning?  If you've been burned by API changes, you're probably the one fussing. If you are a maintainer of an API, you might also be fussing about trying t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-version-a-rest-api/</link>
                <guid isPermaLink="false">66bccb6c4a4c0beb784641d6</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ data contracts ]]>
                    </category>
                
                    <category>
                        <![CDATA[ REST ]]>
                    </category>
                
                    <category>
                        <![CDATA[ REST API ]]>
                    </category>
                
                    <category>
                        <![CDATA[ versioning ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tim Kleier ]]>
                </dc:creator>
                <pubDate>Tue, 03 Mar 2020 01:17:36 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/03/Art-Exhibit-Blog-Banner.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you're not very familiar with APIs, you might be wondering...why all the fuss about API versioning? </p>
<p>If you've been burned by API changes, you're probably the one fussing. If you are a maintainer of an API, you might also be fussing about trying to field challenging questions like these:</p>
<pre><code># Is <span class="hljs-built_in">this</span> version <span class="hljs-number">2</span> <span class="hljs-keyword">of</span> just products or <span class="hljs-keyword">of</span> the entire API?
<span class="hljs-regexp">/v2/</span>products

# What catalyzed the change between v1 and v2? How are they different?
<span class="hljs-regexp">/v1/</span>products
/v2/products
</code></pre><p>These questions around versioning are not easy to answer. It's not always clear as to what <code>v1</code> or <code>v2</code> is referring to. And we should not just make a second version of an endpoint when the first no longer <em>seems</em> to suffice. </p>
<p>There are clear reasons <em>why</em> your API needs to have versioning, and there are clear strategies for <em>how</em> to effectively navigate API changes. </p>
<p>However, I have found that most developers--including myself, until I learned some lessons the hard way--are not aware of these reasons and strategies.</p>
<p>This article seeks to highlight those reasons for versioning and strategies for accomplishing it. We're going to assume a <a target="_blank" href="https://restfulapi.net/">REST</a> API context, as it's a standard for many APIs, and focus on the <em>versioning</em> aspect.</p>
<h2 id="heading-what-is-versioning">What is Versioning?</h2>
<p>We should start with level-setting on what is meant by the term "API versioning". Here's our working definition:</p>
<blockquote>
<p>API versioning is the practice of transparently managing changes to your API.</p>
</blockquote>
<p>Versioning is effective communication around changes to your API, so consumers know what to expect from it. You are delivering data to the public in some fashion and you need to communicate when you change the way that data is delivered.</p>
<p>What this boils down to, in the nitty gritty, is managing data contracts and breaking changes. The former is the primary building block of your API and the latter reveals why versioning is needed.</p>
<h3 id="heading-data-contracts">Data Contracts</h3>
<p>An API is an Application Programming <strong>Interface</strong>, and an interface is a <em>shared</em> boundary to exchange information. The data contract is the heart of this interface.</p>
<blockquote>
<p>A data contract is an agreement on the shape and general content of the request and/or response data. </p>
</blockquote>
<p>To illustrate a data contract, here's a basic JSON response body:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"data"</span>: [
    {
      <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span>,
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Product 1"</span>
    },
    {
      <span class="hljs-attr">"id"</span>: <span class="hljs-number">2</span>,
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Product 2"</span>
    }
  ]
}
</code></pre>
<p>It's an object with a <code>data</code> property that is an array (list) of products, each with an <code>id</code> and <code>name</code> property. But the <code>data</code> property could have just as easily been called <code>body</code>, and the <code>id</code> property on each product could have been a GUID instead of an integer. If a single product was being returned, <code>data</code> could be an object instead of an array. </p>
<p>These seemingly subtle changes would have made for a different agreement, a different contract, regarding the "shape" of the data. The data shape could apply to property names, data types, or even the expected format (JSON vs. XML).</p>
<h2 id="heading-why-is-versioning-needed">Why is Versioning Needed?</h2>
<p>With APIs, something as simple as changing a property name from <code>productId</code> to <code>productID</code> can break things for consumers. This very thing happened to our team last week. </p>
<p>Thankfully, we had tests to catch changes to the API contract. However, we shouldn't have needed those tests, because the maintainers of the API should have known this would be a breaking change.</p>
<h3 id="heading-breaking-changes">Breaking Changes</h3>
<p>This was a breaking change to the agreed upon data contract because their change forced us to change our application as well. </p>
<blockquote>
<p><em>What constitutes a "breaking change" in an API endpoint?</em> Any change to your API contract that forces the consumer to also make a change. </p>
</blockquote>
<p>Breaking changes primarily fit into the following categories:</p>
<ol>
<li>Changing the request/response format (e.g. from XML to JSON)</li>
<li>Changing a property name (e.g. from <code>name</code> to <code>productName</code>) or data type on a property (e.g. from an integer to a float)</li>
<li>Adding a required field on the request (e.g. a new required header or property in a request body)</li>
<li>Removing a property on the response (e.g. removing <code>description</code> from a product)</li>
</ol>
<h3 id="heading-api-change-management">API Change Management</h3>
<p>It is never wise or kind to force consumers of an API to make a change. If you must make a breaking change, that's what versioning is for, and we'll cover the most effective ways to version your application and endpoints. </p>
<p>But first let's briefly discuss how to avoid breaking changes in the first place. We could call this API change management.</p>
<p>Effective change management in the context of an API is summarized by the following principles:</p>
<ul>
<li>Continue support for existing properties/endpoints</li>
<li>Add new properties/endpoints rather than changing existing ones</li>
<li>Thoughtfully sunset obsolete properties/endpoints</li>
</ul>
<p>Here's an example that demonstrates all three of these principles in the context of the response for requesting user data:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"data"</span>: {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Carlos Ray Norris"</span>,     <span class="hljs-comment">// original property</span>
    <span class="hljs-attr">"firstName"</span>: <span class="hljs-string">"Carlos"</span>,           <span class="hljs-comment">// new property</span>
    <span class="hljs-attr">"lastName"</span>: <span class="hljs-string">"Norris"</span>,            <span class="hljs-comment">// new property</span>
    <span class="hljs-attr">"alias"</span>: <span class="hljs-string">"Chuck"</span>,                <span class="hljs-comment">// obsolete property</span>
    <span class="hljs-attr">"aliases"</span>: [<span class="hljs-string">"Chuck"</span>, <span class="hljs-string">"Walker"</span>]   <span class="hljs-comment">// new property</span>
  },
  <span class="hljs-attr">"meta"</span>: {
    <span class="hljs-attr">"fieldNotes"</span>: [
      {
        <span class="hljs-attr">"field"</span>: <span class="hljs-string">"alias"</span>,
        <span class="hljs-attr">"note"</span>: <span class="hljs-string">"Sunsetting on [future date]. Please use aliases."</span>
      }
    ]
  }
}
</code></pre>
<p>In this example, <code>name</code> was an original property. The <code>firstName</code> and <code>lastName</code> fields are being implemented to provide a more granular option, in the event that the consumer wants to display "Mr. Norris" with some string interpolation but without having to parse the <code>name</code> field. However, the <code>name</code> property will be supported in an ongoing fashion. </p>
<p><code>alias</code>, on the other hand, is going to be deprecated in favor of the <code>aliases</code> array--because Chuck has so many aliases--and there is a note in the response to indicate the sunsetting time frame.</p>
<h2 id="heading-how-do-you-version-an-api">How Do You Version an API?</h2>
<p>These principles will take a long way in navigating changes to your API without needing to roll a new version. However, sometimes it's avoidable, and if you need a brand new data contract, you'll need a new version of your endpoint. So you'll need to communicate that to the public in some way.</p>
<p>As an aside, do note that we're not talking about the version of the underlying code base. So if you're using <a target="_blank" href="https://semver.org/">semantic versioning</a> for your application that also supports a public API, you will likely want to separate those versioning systems.</p>
<p>How do you create a new version of your API? What are the different methods for doing so? You'll need to determine what <em>type</em> of versioning strategy you want to take in general, and then as you develop and maintain your API, you'll need to determine the <em>scope</em> of each version change.</p>
<h3 id="heading-scope">Scope</h3>
<p>Let's tackle scope first. As we explored above, sometimes data contracts will be compromised by a breaking change, and that means we'll need to provide a new version of the data contract. That could mean a new version of an endpoint, or it could mean a change at a more global application scope.</p>
<p>We can think of levels of scope change within a tree analogy:</p>
<ul>
<li><strong>Leaf</strong> - A change to an isolated endpoint with no relationship to other endpoints</li>
<li><strong>Branch</strong> - A change to a group of endpoints or a resource accessed through several endpoints</li>
<li><strong>Trunk</strong> - An application-level change, warranting a version change on most or all endpoints</li>
<li><strong>Root</strong> - A change affecting access to all API resources of all versions</li>
</ul>
<p>As you can see, moving from leaf to root, the changes become progressively more impactful and global in scope.</p>
<p>The <em>leaf</em> scope can often be handled through effective API change management. If not, simply create a new endpoint with the new resource data contract.</p>
<p>A <em>branch</em> is a little trickier, depending on just how many endpoints are affected by the data contract change on the resource in question. If the changes are relatively confined to a clear group of related endpoints, you could potentially navigate this by introducing a new name for the resource and updating your docs accordingly.</p>
<pre><code># variants, which has a breaking change, is accessed on multiple routes
/variants
/products/:id/variants

# we introduce product-variants instead
/product-variants
/products/:id/product-variants
</code></pre><p>A <em>trunk</em> refers to application-level changes that are often a result of a change in one of the following categories:</p>
<ul>
<li>Format (e.g. from <a target="_blank" href="https://www.w3schools.com/xml/xml_whatis.asp">XML</a> to <a target="_blank" href="https://www.w3schools.com/js/js_json_intro.asp">JSON</a>)</li>
<li>Specification (e.g. from an in-house one to <a target="_blank" href="https://www.freecodecamp.org/news/p/ccead735-3d4a-4304-b4e2-57b78ce59156/jsonapi.org">JSON API</a> or <a target="_blank" href="https://www.openapis.org/">Open API</a>)</li>
<li>Required headers (e.g. for authentication/authorization)</li>
</ul>
<p>These will necessitate a change in your overall API version, so you should plan carefully and execute the transition well. </p>
<p>A <em>root</em> change will force you to go one step further in ensuring that all consumers of all versions of your API are aware of the change.</p>
<h2 id="heading-types-of-api-versioning">Types of API Versioning</h2>
<p>As we turn to different types of API versioning, we'll want to use these insights into varying scopes of API changes to evaluate the types. Each approach has its own set of strengths and weaknesses in addressing changes based on their scope.</p>
<p>There are several methods for managing the version of your API. URI path versioning is the most common.</p>
<h3 id="heading-uri-path">URI Path</h3>
<pre><code>http:<span class="hljs-comment">//www.example.com/api/v1/products</span>
http:<span class="hljs-comment">//api.example.com/v1/products</span>
</code></pre><p>This strategy involves putting the version number in the path of the URI, and is often done with the prefix "v". More often than not, API designers use it to refer to their application version (i.e. "trunk") rather than the endpoint version (i.e. "leaf" or "branch"), but that's not always a safe assumption.</p>
<p>URI path versioning implies orchestrated releases of application versions that will require one of two approaches: maintaining one version while developing a new one or forcing consumers to wait for new resources until the new version is released. It also means you'd need to carry over any non-changed endpoints from version to version. However, for APIs with relatively low volatility, it's still a decent option.</p>
<p>You would likely not want to relate your version number to that of the endpoint or resource, because it would easily result in something like a <code>v4</code> of <code>products</code> but a <code>v1</code> of <code>variants</code>, which would be rather confusing.</p>
<h3 id="heading-query-params">Query Params</h3>
<pre><code>http:<span class="hljs-comment">//www.example.com/api/products?version=1</span>
</code></pre><p>This type of versioning adds a query param to the request that indicates the version. Very flexible in terms of requesting the version of the resource you'd like at the "leaf" level, but it holds no notion of the overall API's version and lends itself to the same out-of-sync issues mentioned in the above comment on endpoint-level versioning of the URI path.</p>
<h3 id="heading-header">Header</h3>
<pre><code>Accept: version=<span class="hljs-number">1.0</span>
</code></pre><p>The header approach is one that provides more granularity in serving up the requested version of any given resource. </p>
<p>However, it's buried in the request object and isn't as transparent as the URI path option. It's also still hard to tell whether <code>1.0</code> refers to the version of the endpoint or the API itself.</p>
<h3 id="heading-integrating-types">Integrating Types</h3>
<p>Each of these approaches seem to have the weakness of either favoring a "leaf" or "trunk" scope, but not supporting both. </p>
<p>If you need to maintain the overall API version and also provide support for multiple versions of resources, consider a blend of the URI Path and Query Params types, or a more advanced Header approach.</p>
<pre><code># URI path and query params combo
<span class="hljs-attr">http</span>:<span class="hljs-comment">//api.example.com/v1/products?version=1</span>
http:<span class="hljs-comment">//api.example.com/v1/products?version=2</span>

# Extended headers, <span class="hljs-keyword">for</span> http:<span class="hljs-comment">//api.example.com/products</span>
Accept: api-version=<span class="hljs-number">1</span>; resource-version=<span class="hljs-number">1</span>
<span class="hljs-attr">Accept</span>: api-version=<span class="hljs-number">1</span>; resource-version=<span class="hljs-number">2</span>
</code></pre><h2 id="heading-conclusion">Conclusion</h2>
<p>We've covered a lot of ground here, so let's recap:</p>
<ul>
<li>API versioning is the practice of transparently managing changes to your API.</li>
<li>Managing an API boils down to defining and evolving data contracts and dealing with breaking changes.</li>
<li>The most effective way to evolve your API without breaking changes is to follow effective API change management principles.</li>
<li>For most APIs, versioning in the URI path is the most straightforward solution.</li>
<li>For more complex or volatile APIs, you can manage varying scopes of changes by employing an integration of URI path and query params approaches.</li>
</ul>
<p>Although these principles should provide clear direction in how to effectively manage change to your APIs, evolving an API is potentially more of an art than a science. It requires thought and foresight to create and maintain a reliable API.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ API stability is cheap and easy, with Compat Patchers! ]]>
                </title>
                <description>
                    <![CDATA[ By Pakal de Bonchamp Why backwards compatibility matters (If you're already convinced that API stability is a crucial concern, not the whim of a few conservative mummies, save yourself some time and rush to the following chapter.) "WE DO NOT BREAK U... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/api-stability-is-cheap-and-easy/</link>
                <guid isPermaLink="false">66d4608bd7a4e35e384349a5</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ best practices ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Compatibility ]]>
                    </category>
                
                    <category>
                        <![CDATA[ patching ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ stability ]]>
                    </category>
                
                    <category>
                        <![CDATA[ versioning ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 28 Jun 2019 21:00:40 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/06/windowsserver_article_012-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Pakal de Bonchamp</p>
<h2 id="heading-why-backwards-compatibility-matters">Why backwards compatibility matters</h2>
<p><em>(If you're already convinced that API stability is a crucial concern, not the whim of a few conservative mummies, save yourself some time and rush to the following chapter.)</em></p>
<blockquote>
<p>"WE DO NOT BREAK USERSPACE!"</p>
</blockquote>
<p>This <a target="_blank" href="https://lkml.org/lkml/2012/12/23/75">famous rant of Linus Torvalds</a> is, alas, more relevant than ever.</p>
<blockquote>
<p>If a change results in user programs breaking, it's a bug in the kernel. We never EVER blame the user programs. How hard can this be to understand?</p>
<p>If applications didn't care about specific error values, then it wouldn't make sense to have more than one to begin with, and you shouldn't care which one that was. But since applications <em>do</em> care, and since we <em>do</em> have multiple error values, we stick to the old ones, unless there are some <em>very</em> good reasons not to. And those reasons really need to be very good, and spelled out and explained.</p>
</blockquote>
<p>Let's replace "kernel" by any framework/library, and "error value" by "API signature", and we get an invaluable commandment of good programming. We can live quite well with a few buglets and imperfect features, but when our applications deliver segfaults or lengthy tracebacks after a simple version upgrade, there is a problem. A real problem.</p>
<p>And there is a paradox here. Regarding OS distributions, drivers, libc/gtk/Qt and other low-level, statically typed libraries, we expect - and are happy to experience - painless updates, bringing only new features and bugfixes. Whereas for our high-level web frameworks, mostly coded in dynamic languages, we have become resigned to the fact that each update could become a 3-days labor of understanding breakages, finding compatible versions of dependencies, and forking or monkey-patching until test suites are green again. Logically, shouldn't it be the other way round ? Why are most 1995 win32 freewares still working, why was the x32/x64 migration so transparent for most users, if a pluggable server app released 2 years ago is broken on multiple aspects?</p>
<p>I'll tell you why. </p>
<p>API stability was once a very praised commitment. Semantic versioning was a must-have. Projects like Qt proudly detailed the measures they took to ensure their C/C++ would evolve without breakage. Some even filled their function parameters with NULL values "reserved for future uses". And incompatible changes were only evocated when no solution could be found.</p>
<p>Luckily, this mindset is still valid in large areas of programming. But now a different philosophy has contaminated minds, especially in the web industry. This spartan way of thinking could be called "Walk or die", "No paradise for the weak", or "As long as I warn, I can shoot the bullet". Sometimes hiding behind cute concepts like "calendar versioning", or "evergreen applications", this way of thinking is actually crystal-clear: the most minor software upgrade may introduce breaking changes - documented or not, proudly assumed or not -  just deal with it.</p>
<p>Why? Why not just let aliases, adapters, and other compatibility shims, when moving modules around, when renaming objects, when changing function signatures? With the dynamic and introspective nature of most modern languages,  isn't that a breeze? Sometimes development resources are so scarce that it's already too much. But the rest of the time? My guess is that when it is not a problem of technical skill, or of laziness, it might be a cultural issue.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/meme_bill.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Do you know this dopamine hit you get when you tick a box in a checklist, especially the last one? This satisfaction that sometimes makes you write an already completed task <em>just so you can tick it</em>? You might get the same thrill when you brutally refactor code to a cleaner architecture, or erase compatibility shims "<em>as per deprecation policy</em>". It's the happiness rush of the duty well done, of the return to purity. </p>
<p>But this precise feeling is a huge lie. A blinding self-absorption. A harmful psychological bias.</p>
<p>Compatibility shims are not technical debt; nor a set of warts. At the very contrary, they are invaluable assets. With these few chunks of source code, our software extends its compatibility to tens, hundreds, thousands of miscellaneous libraries and applications spread across the web, in both public and private repositories, ranging from tiny utilities to huge corporate applications. These foreign codebases, full of business logic, of money-bringing features, of highly specific code, are what make their foundations worth existing. </p>
<p>But this ecosystem is of an extraordinary diversity. Some repositories get commits every day from multiple contributors, some get a mass update once in a while when their maintainers get a little spare time, and some have not been touched for years (because their creator lost interest, or just couldn't find anything to improve). Some have Tox-like multi-version testing and continuous integration, some don't even have a single unit-test. </p>
<p>So what happens when developers follow this trendy "Walk or die" philosophy? The ecosystem, already heavily fragmented by language (python2vs3, ruby, go, php... just for dynamic languages), by file format and network protocol, by framework and execution style (sync vs async), gets fragmented even more. In the most silent and deadly way. Basically, if we consider the codebases depending on a framework or library (here called "the software"): </p>
<ul>
<li>Repositories that haven't been updated for a few years are broken by default.</li>
<li>Repositories that are actively maintained, but do not target the same software version as us, do not work either.</li>
<li>Bug trackers get filled with useless "Plz add support for version X.Y.Z" or "Plz restore support for version X.Y.Z" tickets.</li>
<li>Forks flourish around mildly renowned repositories ; forks which can't be merged back, since each of their modifications are highly likely to break things for other software versions ; and the subsequent improvements brought by each forker, being chained to divergent codebases, keep spreading without ever being merged back ; naturally, they get remade by several developers each on his own, since few of them take the time to review the forks graph and cherry-pick interesting commits.</li>
<li>Biggest projects sometimes get nice enough to provide a compatibility matrix, or "known working sets" pinned down to their patch version number. But as soon as you have more than a few dependencies, you enter a dependency hell that no conflict resolution algorithm can tackle ; you just have to fork, fork, fork, and monkey-patch, until your dependencies find an agreement.</li>
<li>Project requirements get filled with links to git repository and commit hashes; semantic-less data which will make the next upgrades still more awkwardly experimental; or which will disappear due to an unexpected "force-push".</li>
<li>Without surprise, many maintainers of these pluggable apps don't want to take on them the additional burden of filling their code with special cases, to work around the breakage frenzy of the main software's developers. As a result, the dependency hell keeps expanding unrestrained.</li>
</ul>
<p>So when proudly pluralizing the name of a submodule, when removing a purportedly little-used utility class, when making an optional argument become mandatory, we're not improving anything. We're just murdering practicality for the sake of aesthetical purity. We're recklessly destroying entire regions of the software ecosystem, turning gazillions of test suites into reddish nightmares. But we'll never know to what extent; especially if we don't check. Ignorance is bliss.</p>
<p>Biological ecosystems can reach down to the depth of the abyss or to other planets if they have plenty of time; when things change too quickly, it's mass extinction. Software ecosystems are no different. Enjoying the cleanliness of an API "remade from scratch" is like enjoying the microbial sterility of a forest vitrified by a nuclear blast. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/meme_cant_break_compat.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>For sure, it's a falsehood to think that because we use some open-source software, we're entitled to getting the bugfixes and features that we request. But it's equally false to think that because the users of our framework/library are not paying clients, we owe them nothing. They trusted us, built their own code against ours, followed our conventions and best practices, when they could have chosen another language/framework/library. How can we justify trampling their own efforts, making them waste days or weeks of development, just because we suddenly felt the irrepressible urge to change a naming scheme, or drop perfectly working code? We're all interdependent in a software ecosystem, and a tiny dose of awareness, carefulness and rationality can go a long way. "<em>But you're never sure that a change doesn't break things</em>", some will shrug. Sure. No one demands perfection. But not being deliberately harmful is already a very good start. </p>
<p>There is like a paternalistic mood behind some advocates of the "walk or die" approach. "<em>If we stick to API stability, contributors will get lazy and never update their modules, the ecosystem will rot in place instead of moving forward</em>". Oh damn, how dangerous it is, to do people's good against their will. If we want users to update their codebase, at the very contrary, we should start by not brutally breaking things. We should bring shiny new features, not just a gun to their head. We should let them fix mildly annoying "deprecation warnings" when they feel like and can, not <strong>right now</strong>. We should let them spend their time on useful contributions, not on repairing what we have broken with the planned obsolescence that we dare calling "progress". </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/Compatibility-of-Hardware-and-Software.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Fun facts: advertising breaking changes in release notes does not grant them legitimacy; and when a private API is so handy that it's used by multiple projects, maybe it's a sign that it should be made public and documented, not that it should be wrecked at the first impulse.</p>
<p>So let's carve it into the marble of our desktops: API stability is, must be, on top of our list of concerns; along with robustness and adequation with final users' needs; but infinitely above any kind of aesthetical consideration. Only with long-term compatibility may our software ecosystems grow from a short elite of continually updated applications, to a huge and diverse galaxy of modules; some last- updated yesterday, some last-updated ten years ago, but all of them <strong>getting stuff done</strong>. Because that what software is all about, and that's what people are being paid for, at the end of the day. Not wasting time fixing the wheel that worked yesterday, and won't tomorrow ever more, because we heard the calling of the void.</p>
<h2 id="heading-the-compat-patcher-concept">The Compat Patcher concept</h2>
<p>Am I asking (open source) software maintainers to make more efforts, in order to achieve this so crucial long-term API stability? Nope. I wouldn't dare. At the contrary, I ask them to be lazier. But the good, the sensible, the both self-interested and benevolent, kind of lazy.</p>
<p>More compatibility means less support requests, less time justifying controversial changes, and more feature/bugfix contributions from our community. More compatibility doesn't even mean more keyboard typing. Except if we're only into toy projects, we've already put compatibility shims in place for a time. Just one thing to do : <strong>leave them be</strong>. They don't hurt us. They don't waste disk space. They are probably worth thousands of dollars per character. Let's just avoid one savage "Removing compatibility shim XYZ" commit, and move on towards greater goals.</p>
<p>And if the mere view of a compatibility shim makes us vomit (a not so unusual pathology I guess), there is awesome news : with high-level languages, we don't need shims in our code anymore. We just have to embrace the concept of <strong>Compatibility Patcher</strong> (or "compatcher" for neologism lovers).</p>
<p>Wat-iz-dat ? Just a companion library, often living its own life in its own repository, which plugs itself to the real software at startup time, and restores its compatibility with a decade of its previous versions. Thus, we can keep our codebase entirely unaware of whatever a "deprecation" might be, while still keeping a symbiotic coexistence with the thousands modules living in our ecosystem.</p>
<p>Monkey-patching is ugly, someone said? Maybe, but never as ugly as spending hours retro-engineering a whole plugin architecture, just to realize that an additional "S" in naming conventions sufficed to ruin everything. Programming overlords might prefer to weave their code with external shims using <a target="_blank" href="https://en.wikipedia.org/wiki/Aspect-oriented_programming">aspect-oriented programming</a>, but for most of us mere mortals, simplicity and pragmatism are widely sufficient enough. Some documentation, logging, and console warnings, are "explicit" enough to make anybody keep control over the codebase.</p>
<p>Let's not underestimate the power of high level languages. Examples with Python. We rename a submodule? Fine, thanks to import hooks, "<em>from framework import oldmodule</em>" and "<em>from framework import newmodule</em>" will return the exact same object. We change the signature of a function ? One tiny injection later, the old set of call parameters will automatically be adapted and forwarded to the new signature. We move an entire group of utilities out of the main repository? Fine, but as long as needed, the compatibility patcher will fetch them from their new location, and inject them back where they once so nicely belonged. We rename constants, classes, functions ? Leaving an alias was only costing a single code line, now with compatibility patchers this line doesn't even have to hurt our eyes and hearts anymore.</p>
<p>Please notice, Compatibility Patchers act like time travelers. They work even when developers delete a function, re-add it under a different shape, then remove it again. They work even when developers recklessly modify function behaviours, for example by exchanging similar arguments in-place. So imagine when developers cooperate with this system, and nicely decouple programming concerns so that the patch is minimal!</p>
<p>Cherry on the cake, by separating "state of art" code and compatibility shims, Compat Patchers make it a breeze to <strong>selectively activate compatibility sets</strong>. Your project is brand new and only relies on bleeding edge libraries ? Fine, deactivate the whole patcher. You just need compatibility with the last two major versions of the framework? Just enable the corresponding families of shims. You need support for very old packages ? Leave the patcher configuration in maximal mode.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/unicorn-deviant.PNG" alt="Image" width="600" height="400" loading="lazy">
<em>That feeling when all 30 dependencies work smoothly on first install</em></p>
<p>Now comes the anxiety-provoking part : what are the downsides of Compat Patchers? Answer: up to a few seconds of delay at startup (when all shims are activated), and a few logical operations and type checks here and there at runtime. That's it. In the modern web world, where most server processes run for hours uninterrupted, process the most unoptimized (text-based) formats conceivable, and where performance depends much more on DB optimization and proper caching than on raw execution speed, it sounds like a legitimate expense, doesn't it?</p>
<p><em>Edit</em>: another limitation of Compat Patchers is that they require a tiny little bit of cooperation from core developers. Indeed, we all implicitly know the rule of backwards compatibility: "<strong>Only Add Optiona</strong>l". Add new optional arguments, add new functions, add new modules; don't remove elements and behaviours, don't make old options mandatory. But there is an additional, golden rule: <strong>don't ever, ever, change the semantic of things in-place</strong>. If we change the meaning of an argument, the format of an output, the action of a callable, without any additional indicator, then setting up shims becomes (almost) unfeasible; in this case, even Compat Patchers won't be able to resolve this ambiguity, and guess the behaviour that our users were requesting when using our API. Let's blacklist forever these kinds of <a target="_blank" href="https://docs.djangoproject.com/en/2.2/releases/2.2/#admin-actions-are-no-longer-collected-from-base-modeladmin-classes">brutal</a> and <a target="_blank" href="https://serverfault.com/questions/829754/why-did-the-format-of-nginx-ssl-client-i-dn-suddenly-change">inimical</a> changes.</p>
<h2 id="heading-time-for-practice">Time for practice!</h2>
<p>Compat patchers are not just wishful thinking. </p>
<p><a target="_blank" href="https://github.com/pakal/django-compat-patcher">Here is one</a> for the famous Django web framework. With a few dozens of small fixers, it allows one to use pluggable apps targeting from versions 1.6 to 2.2 of the framework. And it's only a start - feature requests and comments are welcome. </p>
<p>This patcher is used in production on a few sites, including the <a target="_blank" href="https://github.com/ChrysalisTeam/pychronia">Pychronia</a> portal and its CMS/Blog ecosystem. It runs on <a target="_blank" href="https://github.com/pakal/compat-patcher-core">CompatPatcherCore</a>, a Python micro-framework for creating such companion applications in the blink of an eye (a <a target="_blank" href="https://compat-patcher-core.readthedocs.io/en/latest/startup.html">cookiecutter recipe</a> is even included). </p>
<p>Without surprise, I warmly encourage you to bootstrap a Compat Patcher for the framework/library you might maintain, unless you are one of the few valiant minds already strongly committed to API stability.</p>
<p>This concept should also be easy to port to Ruby, PHP, Javascript, and other high-malleability languages. With lower-level and static languages, the task might be much harder (and require macro processors and the likes), but who knows.</p>
<p><strong>So here we are. Update breakages are not a fatality. Just a bad habit that we must <em>break</em>, thanks to a little pondering and a few technical niceties. We may thus enjoy the delights of ever-growing and ever-working software ecosystems, those which do make development fun and exciting!</strong> </p>
<p><em>Edit 2019/07/05: Evocate the "hard scarcity of resources" as a possible reason for lack of shims, and tweak the tone of this part.</em></p>
<p><em>Edit 2019/07/14: Fix typos, and warn against in-place semantic changes.</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/06/windowsserver_article_012.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
