

The Expand and Contract Pattern in JavaScript | by Fernando Doglio | May, 2021 |...
source link: https://blog.bitsrc.io/versioning-your-components-through-the-expand-contract-pattern-bc31afaae623
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

A practical example: updating my component
Let’s take a look at it from a practical PoV: I’m going to update a composite component I created recently using Bit, for a different article talking about independent components and the composite pattern.
My composite componentThe current interface for my component right now is this one:
But the problem is that I’m now realizing that the traverse
method makes no sense to have it go through the entire thing. I want it to apply the function fn
to a particular node, using the name as ID.
The problem? This is definitely a breaking change. If I were to update that code and release the new version, every developer out there thinking they should be using the latest version “because it’s probably better” would have their code stop working.
Even worse, I would’ve stopped supporting their use case out of the sudden without prior notice. This is terrible DX!
Instead, I can Expand & Contract my code. Let me explain what that means:
There are 3 stages to this pattern: Expand, Adapt and Contract.

The Expand phase
During the first one, the “Expand” phase, I’ll have to add the code I want as my final version but I want it to coexist with the current implementation. Essentially I want to grow (thus the “expand” name) my code base into supporting both versions (the new and the old).
This is not a particularly easy feat to accomplish, depending on the type of project and the technology you’re working with, this can be as simple as providing an overloaded version of a method or as complex as having a single endpoint of an API do two different things.
The point of this phase is that it is NOT a permanent fix. The expanded state is not going to last, but it’ll give your users enough time to go into the Adapt phase without breaking their entire code base without a single warning.
In my case, my component’s traverse
method will now change its signature to look like this:
Notice how I added a second, optional, attribute. This attribute is, for the time being, only to be used if present, thus my code would have to adapt into something like this:
Now I’m checking if the name parameter is present and if it is, I’m verifying the name matches, otherwise if it’s not present I’m also applying the function (like I did before).
This implementation is still valid for the old test cases I had, I can check that by running bit test
on my terminal. These tests, as you can see here were only interested in the old behavior (no name is used).
Now I should also add a new test to ensure the new logic also works, so I can add something like this:
I am now using my traverse
method to capture the color of my chairs. And Bit is telling me that it works:
$ bit test
- loading bit...
- loading aspects...
- running command test [pattern]...
testing total of 1 components in workspace 'my-workspace-name'
testing 1 components with environment teambit.harmony/node
PASS components/composite/composite.spec.ts
Composite component
√ should correctly add a component to the children list (2 ms)
√ should iterate over the full composite children-first (1 ms)
√ should iterate over the entire composite and apply the function only to the matching name node
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 2.033 s
Ran all test suites.
tested 1 components in 3.655 seconds.
I’m ready to release this version now. However, this is not my final version, as I started this journey trying to update the method into not supporting a full traversal anymore.
I can release a new version using Bit by typing:
$ bit tag --all 0.0.3
$ bit export
This will effectively release version 0.0.3 which is still backward compatible. After this release, I’ll allow for the Adoption phase to start.
The Adoption
This is when my users will adapt their code into supporting the new version while still having their current version work. Does that make any sense?
Essentially through the adoption phase, I’m allowing them to “future proof” their code. I can do this by showing them exactly what the new behavior will look like, without removing the old one.
During this phase I will also have to make sure I correctly communicate the future deprecation of the old behavior. I can do that through updates on the documentation, code comments or any other means of notification you can think of. However you choose to do it, remember that this is the time. You’re not just giving your users time to adapt, you’re also buying some time to properly notify them of the future breaking change.
Look at the new version of my docs for this release:

For version 0.0.3 I’ve added a deprecation warning to make sure it’s clear for all users that my traverse
method is going to change.
Once you’ve given them enough information and time to adjust, the “Contract” part begins.
The Contract phase
As you can probably imagine, this is where the code changes one more time, and now is when the actual breaking change is published. My example was very basic, but consider having a bigger set of parallel features to be maintained over time.
You can’t live in the Adoption phase for too long, you have to finish what you started, so in the end, you need to remove backward compatibility and clean up your code, API or whatever it is you’re publishing.
During the contract phase you’ll strip away all the extra code you added to support both versions. In my case, that looks like this:
The change is minimal in this example, but you can see how the signature of the method changed now to have the name
attribute be mandatory (it didn’t change in the sense of adding extra attributes or anything, that would not be allowed at this point). And the code changed as well, the IF
statement is now simpler, since the name
attribute is now going to be present all the time.
In fact, with this new change, one of the tests will fail because we’re clearly no longer supporting the old version (calling traverse
with only one parameter):

That code is no longer valid, so we need to either update it accordingly or remove the test altogether. Since I’m already testing this method on another test, I’ll remove this one.
And I’m now ready to publish the newest version. However, if you’re using a versioning scheme such as Semver remember to properly update the major version, since we’re definitely pushing a breaking change now.
In my case, since I’m using Bit, I can do that easily by running:
$ bit tag 1.0.0
$ bit export
Recommend
-
35
A couple of weeks ago I presented at Percona Universi...
-
46
Last Friday, legendary MIT computer scientist Fernando “Corby” Corbató passed away at his home in Newton, Massachusetts. He was 93. The Oakland-born researcher was responsible for several pivotal advances in the...
-
8
Understanding Peer Dependencies in JavaScriptPeer dependencies are a must-know feature that many developers overlook
-
9
What’s Wrong with Classes in JavaScript?I’m all for syntactic sugar, but we’re still missing a few things.
-
4
A Deep Dive Into JavaScript ModulesUnderstanding the various JavaScript module types.
-
7
Top 5 Embedded Databases for JavaScript ApplicationsWhat do you use when you only need to store small amounts of dataImage by
-
9
Your JavaScript SmellsCommon code smells, what they are and how to avoid themIt’s time to address the pink elephant in the room. I’m sorry, I tried to ignore it but this is turning into a prob...
-
8
How to expand and contract height of a UITableView cell when tapped To avoid confusion, this article focuses on expanding and contracing the height of a single tableview cell wh...
-
6
Bunzz Raises $4.5M Seed Round to Expand its Smart Contract Hub for DApp Development January 24, 2023
-
9
Expand Contract for Databases and ServicesMay 18, 2023 · 4 minI haven’t seen Expand-Contract written about in some years, and I think it is a great way of performing database schema migrations without the need...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK