diff --git a/lib/datasource/index.js b/lib/datasource/index.js
index 6400ed8baee9ed7bcee7a98d289552553489bba5..b244dc11f4d24cdbaba27a7f9321351b7c59c038 100644
--- a/lib/datasource/index.js
+++ b/lib/datasource/index.js
@@ -57,14 +57,14 @@ async function getPkgReleases(config) {
   const versionScheme =
     config && config.versionScheme ? config.versionScheme : 'semver';
   // Filter by version scheme
-  const { isVersion, sortVersions } = versioning.get(versionScheme);
+  const version = versioning.get(versionScheme);
   // Return a sorted list of valid Versions
   function sortReleases(release1, release2) {
-    return sortVersions(release1.version, release2.version);
+    return version.sortVersions(release1.version, release2.version);
   }
   if (res.releases) {
     res.releases = res.releases
-      .filter(release => isVersion(release.version))
+      .filter(release => version.isVersion(release.version))
       .sort(sortReleases);
   }
   return res;
diff --git a/lib/util/package-rules.js b/lib/util/package-rules.js
index ea8341cef9ecc70e421cb5ad310ad5178d1b5719..3c664cc816104922489df5e9f38af07e7fa7edca 100644
--- a/lib/util/package-rules.js
+++ b/lib/util/package-rules.js
@@ -174,13 +174,13 @@ function matchesRule(inputConfig, packageRule) {
     positiveMatch = true;
   }
   if (matchCurrentVersion) {
-    const { matches, isVersion } = versioning.get(versionScheme);
+    const version = versioning.get(versionScheme);
     const compareVersion =
-      currentValue && isVersion(currentValue)
+      currentValue && version.isVersion(currentValue)
         ? currentValue // it's a version so we can match against it
         : lockedVersion || fromVersion; // need to match against this fromVersion, if available
-    if (compareVersion && isVersion(compareVersion)) {
-      const isMatch = matches(compareVersion, matchCurrentVersion);
+    if (compareVersion && version.isVersion(compareVersion)) {
+      const isMatch = version.matches(compareVersion, matchCurrentVersion);
       // istanbul ignore if
       if (!isMatch) {
         return false;
diff --git a/lib/workers/pr/changelog/index.js b/lib/workers/pr/changelog/index.js
index 2d3d3976df492720a459cfd55df69658ad0b95b2..459cbfee1cc7a2737e73985d1c7a5a6f17ad28cd 100644
--- a/lib/workers/pr/changelog/index.js
+++ b/lib/workers/pr/changelog/index.js
@@ -15,8 +15,8 @@ async function getChangeLogJSON(args) {
   // releases is too noisy in the logs
   const { releases, ...param } = args; // eslint-disable-line @typescript-eslint/no-unused-vars
   logger.debug({ args: param }, `getChangeLogJSON(args)`);
-  const { equals } = versioning.get(versionScheme);
-  if (!fromVersion || equals(fromVersion, toVersion)) {
+  const version = versioning.get(versionScheme);
+  if (!fromVersion || version.equals(fromVersion, toVersion)) {
     return null;
   }
   try {
diff --git a/lib/workers/pr/changelog/source-github.js b/lib/workers/pr/changelog/source-github.js
index 6f138cfa73b9c7e21984772402a2a9cec141d6fb..a7c30da4d6815a75e3a89103b1d5a145ff5c68f2 100644
--- a/lib/workers/pr/changelog/source-github.js
+++ b/lib/workers/pr/changelog/source-github.js
@@ -53,9 +53,7 @@ async function getChangeLogJSON({
     logger.debug('No release notes for @types');
     return null;
   }
-  const { isVersion, equals, isGreaterThan, sortVersions } = versioning.get(
-    versionScheme
-  );
+  const version = versioning.get(versionScheme);
   const { protocol, host, pathname } = URL.parse(sourceUrl);
   const githubBaseURL = `${protocol}//${host}/`;
   const url = sourceUrl.startsWith('https://github.com/')
@@ -84,8 +82,8 @@ async function getChangeLogJSON({
   }
   // This extra filter/sort should not be necessary, but better safe than sorry
   const validReleases = [...releases]
-    .filter(release => isVersion(release.version))
-    .sort((a, b) => sortVersions(a.version, b.version));
+    .filter(release => version.isVersion(release.version))
+    .sort((a, b) => version.sortVersions(a.version, b.version));
 
   if (validReleases.length < 2) {
     logger.debug('Not enough valid releases');
@@ -100,8 +98,8 @@ async function getChangeLogJSON({
     }
     const regex = new RegExp(`${depName}[@-]`);
     const tagName = tags
-      .filter(tag => isVersion(tag.replace(regex, '')))
-      .find(tag => equals(tag.replace(regex, ''), release.version));
+      .filter(tag => version.isVersion(tag.replace(regex, '')))
+      .find(tag => version.equals(tag.replace(regex, ''), release.version));
     if (tagName) {
       return tagName;
     }
@@ -118,8 +116,9 @@ async function getChangeLogJSON({
 
   const changelogReleases = [];
   // compare versions
-  const include = version =>
-    isGreaterThan(version, fromVersion) && !isGreaterThan(version, toVersion);
+  const include = v =>
+    version.isGreaterThan(v, fromVersion) &&
+    !version.isGreaterThan(v, toVersion);
   for (let i = 1; i < validReleases.length; i += 1) {
     const prev = validReleases[i - 1];
     const next = validReleases[i];
diff --git a/lib/workers/pr/pr-body.js b/lib/workers/pr/pr-body.js
index b400bb210cc662a01b7d6b8561e1bcdc1ca26100..29bedd2f50c0087c97f675a871c25335969d936a 100644
--- a/lib/workers/pr/pr-body.js
+++ b/lib/workers/pr/pr-body.js
@@ -79,8 +79,8 @@ async function getPrBody(config) {
     // istanbul ignore if
     if (updateType === 'minor') {
       try {
-        const { getMinor } = versioning.get(versionScheme);
-        if (getMinor(fromVersion) === getMinor(toVersion)) {
+        const version = versioning.get(versionScheme);
+        if (version.getMinor(fromVersion) === version.getMinor(toVersion)) {
           upgrade.updateType = 'patch';
         }
       } catch (err) {
diff --git a/lib/workers/repository/init/vulnerability.js b/lib/workers/repository/init/vulnerability.js
index 0b81bffe1efdfed7e7f96fc51a0f37e4eeba8c83..19b763e5f555a8eb4c23a79ee56bd4533811a844 100644
--- a/lib/workers/repository/init/vulnerability.js
+++ b/lib/workers/repository/init/vulnerability.js
@@ -70,11 +70,11 @@ async function detectVulnerabilityAlerts(input) {
         pip_requirements: 'pep440',
         rubygems: 'ruby',
       };
-      const versionScheme = versioning.get(versionSchemes[datasource]);
-      if (versionScheme.isVersion(firstPatchedVersion)) {
+      const version = versioning.get(versionSchemes[datasource]);
+      if (version.isVersion(firstPatchedVersion)) {
         if (combinedAlerts[datasource][depName].firstPatchedVersion) {
           if (
-            versionScheme.isGreaterThan(
+            version.isGreaterThan(
               firstPatchedVersion,
               combinedAlerts[datasource][depName].firstPatchedVersion
             )
diff --git a/lib/workers/repository/process/lookup/filter.js b/lib/workers/repository/process/lookup/filter.js
index a5d3a5a68dfb79416347f245025960aa39e40f9f..dc8f7555cc9b7cf8d906db662f7f37eadb8bddee 100644
--- a/lib/workers/repository/process/lookup/filter.js
+++ b/lib/workers/repository/process/lookup/filter.js
@@ -20,34 +20,24 @@ function filterVersions(
     respectLatest,
     allowedVersions,
   } = config;
-  const {
-    getMajor,
-    getMinor,
-    getPatch,
-    isGreaterThan,
-    isStable,
-    isValid,
-    matches,
-  } = versioning.get(versionScheme);
+  const version = versioning.get(versionScheme);
   if (!fromVersion) {
     return [];
   }
 
   // Leave only versions greater than current
-  let filteredVersions = versions.filter(version =>
-    isGreaterThan(version, fromVersion)
+  let filteredVersions = versions.filter(v =>
+    version.isGreaterThan(v, fromVersion)
   );
 
   // Don't upgrade from non-deprecated to deprecated
   const fromRelease = releases.find(release => release.version === fromVersion);
   if (ignoreDeprecated && fromRelease && !fromRelease.isDeprecated) {
-    filteredVersions = filteredVersions.filter(version => {
-      const versionRelease = releases.find(
-        release => release.version === version
-      );
+    filteredVersions = filteredVersions.filter(v => {
+      const versionRelease = releases.find(release => release.version === v);
       if (versionRelease.isDeprecated) {
         logger.debug(
-          `Skipping ${config.depName}@${version} because it is deprecated`
+          `Skipping ${config.depName}@${v} because it is deprecated`
         );
         return false;
       }
@@ -56,19 +46,19 @@ function filterVersions(
   }
 
   if (allowedVersions) {
-    if (isValid(allowedVersions)) {
-      filteredVersions = filteredVersions.filter(version =>
-        matches(version, allowedVersions)
+    if (version.isValid(allowedVersions)) {
+      filteredVersions = filteredVersions.filter(v =>
+        version.matches(v, allowedVersions)
       );
     } else if (versionScheme !== 'npm' && semver.validRange(allowedVersions)) {
       logger.debug(
         { depName: config.depName },
         'Falling back to npm semver syntax for allowedVersions'
       );
-      filteredVersions = filteredVersions.filter(version =>
+      filteredVersions = filteredVersions.filter(v =>
         semver.satisfies(
           // @ts-ignore
-          semver.coerce(version),
+          semver.coerce(v),
           allowedVersions
         )
       );
@@ -86,19 +76,19 @@ function filterVersions(
   }
 
   // if current is unstable then allow unstable in the current major only
-  if (!isStable(fromVersion)) {
+  if (!version.isStable(fromVersion)) {
     // Allow unstable only in current major
     return filteredVersions.filter(
-      version =>
-        isStable(version) ||
-        (getMajor(version) === getMajor(fromVersion) &&
-          getMinor(version) === getMinor(fromVersion) &&
-          getPatch(version) === getPatch(fromVersion))
+      v =>
+        version.isStable(v) ||
+        (version.getMajor(v) === version.getMajor(fromVersion) &&
+          version.getMinor(v) === version.getMinor(fromVersion) &&
+          version.getPatch(v) === version.getPatch(fromVersion))
     );
   }
 
   // Normal case: remove all unstable
-  filteredVersions = filteredVersions.filter(isStable);
+  filteredVersions = filteredVersions.filter(version.isStable);
 
   // Filter the latest
 
@@ -112,10 +102,8 @@ function filterVersions(
     return filteredVersions;
   }
   // No filtering if fromVersion is already past latest
-  if (isGreaterThan(fromVersion, latestVersion)) {
+  if (version.isGreaterThan(fromVersion, latestVersion)) {
     return filteredVersions;
   }
-  return filteredVersions.filter(
-    version => !isGreaterThan(version, latestVersion)
-  );
+  return filteredVersions.filter(v => !version.isGreaterThan(v, latestVersion));
 }
diff --git a/lib/workers/repository/process/lookup/index.js b/lib/workers/repository/process/lookup/index.js
index 5cce99116fba76f9b2ae8c221fdb3e6012bddf7c..e6bef79053833bb64c9736d5617a407a639779d0 100644
--- a/lib/workers/repository/process/lookup/index.js
+++ b/lib/workers/repository/process/lookup/index.js
@@ -18,22 +18,10 @@ module.exports = {
 async function lookupUpdates(config) {
   const { depName, currentValue, lockedVersion } = config;
   logger.trace({ dependency: depName, currentValue }, 'lookupUpdates');
-  const {
-    equals,
-    getMajor,
-    getMinor,
-    isGreaterThan,
-    isSingleVersion,
-    isCompatible,
-    isValid,
-    isVersion,
-    matches,
-    getNewValue,
-    valueToVersion,
-  } = versioning.get(config.versionScheme);
+  const version = versioning.get(config.versionScheme);
   /** @type any */
   const res = { updates: [], warnings: [] };
-  if (isValid(currentValue)) {
+  if (version.isValid(currentValue)) {
     const dependency = clone(await getPkgReleases(config));
     if (!dependency) {
       // If dependency lookup fails then warn and return
@@ -72,7 +60,7 @@ async function lookupUpdates(config) {
     // Filter out any results from datasource that don't comply with our versioning scheme
     let allVersions = releases
       .map(release => release.version)
-      .filter(v => isVersion(v));
+      .filter(v => version.isVersion(v));
     // istanbul ignore if
     if (allVersions.length === 0) {
       const message = `Found no results from datasource that look like a version`;
@@ -93,12 +81,13 @@ async function lookupUpdates(config) {
       allVersions = allVersions.filter(
         v =>
           v === taggedVersion ||
-          (v === currentValue && isGreaterThan(taggedVersion, currentValue))
+          (v === currentValue &&
+            version.isGreaterThan(taggedVersion, currentValue))
       );
     }
     // Check that existing constraint can be satisfied
-    const allSatisfyingVersions = allVersions.filter(version =>
-      matches(version, currentValue)
+    const allSatisfyingVersions = allVersions.filter(v =>
+      version.matches(v, currentValue)
     );
     if (config.rollbackPrs && !allSatisfyingVersions.length) {
       const rollback = getRollbackUpdate(config, allVersions);
@@ -119,18 +108,18 @@ async function lookupUpdates(config) {
     if (
       fromVersion &&
       rangeStrategy === 'pin' &&
-      !isSingleVersion(currentValue)
+      !version.isSingleVersion(currentValue)
     ) {
       res.updates.push({
         updateType: 'pin',
         isPin: true,
-        newValue: getNewValue(
+        newValue: version.getNewValue(
           currentValue,
           rangeStrategy,
           fromVersion,
           fromVersion
         ),
-        newMajor: getMajor(fromVersion),
+        newMajor: version.getMajor(fromVersion),
       });
     }
     let filterStart = fromVersion;
@@ -145,15 +134,15 @@ async function lookupUpdates(config) {
       dependency.latestVersion,
       allVersions,
       releases
-    ).filter(version =>
+    ).filter(v =>
       // Leave only compatible versions
-      isCompatible(version, currentValue)
+      version.isCompatible(v, currentValue)
     );
     const buckets = {};
     for (const toVersion of filteredVersions) {
       const update = { fromVersion, toVersion };
       try {
-        update.newValue = getNewValue(
+        update.newValue = version.getNewValue(
           currentValue,
           rangeStrategy,
           fromVersion,
@@ -184,17 +173,17 @@ async function lookupUpdates(config) {
         update.displayTo = toVersion;
         update.isSingleVersion = true;
       }
-      update.newMajor = getMajor(toVersion);
-      update.newMinor = getMinor(toVersion);
+      update.newMajor = version.getMajor(toVersion);
+      update.newMinor = version.getMinor(toVersion);
       update.updateType =
         update.updateType || getType(config, update.fromVersion, toVersion);
       update.isSingleVersion =
-        update.isSingleVersion || !!isSingleVersion(update.newValue);
-      if (!isVersion(update.newValue)) {
+        update.isSingleVersion || !!version.isSingleVersion(update.newValue);
+      if (!version.isVersion(update.newValue)) {
         update.isRange = true;
       }
       const updateRelease = releases.find(release =>
-        equals(release.version, toVersion)
+        version.equals(release.version, toVersion)
       );
       // TODO: think more about whether to just Object.assign this
       const releaseFields = [
@@ -211,7 +200,9 @@ async function lookupUpdates(config) {
 
       const bucket = getBucket(config, update);
       if (buckets[bucket]) {
-        if (isGreaterThan(update.toVersion, buckets[bucket].toVersion)) {
+        if (
+          version.isGreaterThan(update.toVersion, buckets[bucket].toVersion)
+        ) {
           buckets[bucket] = update;
         }
       } else {
@@ -253,14 +244,14 @@ async function lookupUpdates(config) {
         });
       }
     }
-    if (valueToVersion) {
+    if (version.valueToVersion) {
       for (const release of res.releases || []) {
-        release.version = valueToVersion(release.version);
+        release.version = version.valueToVersion(release.version);
       }
       for (const update of res.updates || []) {
-        update.newVersion = valueToVersion(update.newValue);
-        update.fromVersion = valueToVersion(update.fromVersion);
-        update.toVersion = valueToVersion(update.toVersion);
+        update.newVersion = version.valueToVersion(update.newValue);
+        update.fromVersion = version.valueToVersion(update.fromVersion);
+        update.toVersion = version.valueToVersion(update.toVersion);
       }
     }
     // update digest for all
@@ -281,11 +272,11 @@ async function lookupUpdates(config) {
     const { updateType, fromVersion, toVersion } = update;
     if (['bump', 'lockfileUpdate'].includes(updateType)) {
       update[updateType === 'bump' ? 'isBump' : 'isLockfileUpdate'] = true;
-      if (getMajor(toVersion) > getMajor(fromVersion)) {
+      if (version.getMajor(toVersion) > version.getMajor(fromVersion)) {
         update.updateType = 'major';
       } else if (
         config.separateMinorPatch &&
-        getMinor(toVersion) === getMinor(fromVersion)
+        version.getMinor(toVersion) === version.getMinor(fromVersion)
       ) {
         update.updateType = 'patch';
       } else {
@@ -314,14 +305,14 @@ async function lookupUpdates(config) {
 
 function getType(config, fromVersion, toVersion) {
   const { versionScheme, rangeStrategy, currentValue } = config;
-  const { getMajor, getMinor, matches } = versioning.get(versionScheme);
-  if (rangeStrategy === 'bump' && matches(toVersion, currentValue)) {
+  const version = versioning.get(versionScheme);
+  if (rangeStrategy === 'bump' && version.matches(toVersion, currentValue)) {
     return 'bump';
   }
-  if (getMajor(toVersion) > getMajor(fromVersion)) {
+  if (version.getMajor(toVersion) > version.getMajor(fromVersion)) {
     return 'major';
   }
-  if (getMinor(toVersion) > getMinor(fromVersion)) {
+  if (version.getMinor(toVersion) > version.getMinor(fromVersion)) {
     return 'minor';
   }
   if (config.separateMinorPatch) {
@@ -354,26 +345,23 @@ function getBucket(config, update) {
 
 function getFromVersion(config, rangeStrategy, allVersions) {
   const { currentValue, lockedVersion, versionScheme } = config;
-  const {
-    isSingleVersion,
-    isVersion,
-    maxSatisfyingVersion,
-    minSatisfyingVersion,
-  } = versioning.get(versionScheme);
-  if (isVersion(currentValue)) {
+  const version = versioning.get(versionScheme);
+  if (version.isVersion(currentValue)) {
     return currentValue;
   }
-  if (isSingleVersion(currentValue)) {
+  if (version.isSingleVersion(currentValue)) {
     return currentValue.replace(/=/g, '').trim();
   }
   logger.trace(`currentValue ${currentValue} is range`);
   if (rangeStrategy === 'pin') {
-    return lockedVersion || maxSatisfyingVersion(allVersions, currentValue);
+    return (
+      lockedVersion || version.maxSatisfyingVersion(allVersions, currentValue)
+    );
   }
   if (rangeStrategy === 'bump') {
     // Use the lowest version in the current range
-    return minSatisfyingVersion(allVersions, currentValue);
+    return version.minSatisfyingVersion(allVersions, currentValue);
   }
   // Use the highest version in the current range
-  return maxSatisfyingVersion(allVersions, currentValue);
+  return version.maxSatisfyingVersion(allVersions, currentValue);
 }
diff --git a/lib/workers/repository/process/lookup/rollback.js b/lib/workers/repository/process/lookup/rollback.js
index 83d657e55210ed3bd75515dcf53278370b1d78c0..7d14e3daf29996e51ad84c955bb4d10d358f842e 100644
--- a/lib/workers/repository/process/lookup/rollback.js
+++ b/lib/workers/repository/process/lookup/rollback.js
@@ -7,22 +7,17 @@ module.exports = {
 
 function getRollbackUpdate(config, versions) {
   const { packageFile, versionScheme, depName, currentValue } = config;
-  const {
-    getMajor,
-    isLessThanRange,
-    getNewValue,
-    sortVersions,
-  } = versioning.get(versionScheme);
+  const version = versioning.get(versionScheme);
   // istanbul ignore if
-  if (!isLessThanRange) {
+  if (!version.isLessThanRange) {
     logger.info(
       { versionScheme },
       'Current version scheme does not support isLessThanRange()'
     );
     return null;
   }
-  const lessThanVersions = versions.filter(version =>
-    isLessThanRange(version, currentValue)
+  const lessThanVersions = versions.filter(v =>
+    version.isLessThanRange(v, currentValue)
   );
   // istanbul ignore if
   if (!lessThanVersions.length) {
@@ -40,7 +35,7 @@ function getRollbackUpdate(config, versions) {
     { dependency: depName, versions },
     'Versions found before rolling back'
   );
-  lessThanVersions.sort(sortVersions);
+  lessThanVersions.sort(version.sortVersions);
   const toVersion = lessThanVersions.pop();
   // istanbul ignore if
   if (!toVersion) {
@@ -48,7 +43,12 @@ function getRollbackUpdate(config, versions) {
     return null;
   }
   let fromVersion;
-  const newValue = getNewValue(currentValue, 'replace', fromVersion, toVersion);
+  const newValue = version.getNewValue(
+    currentValue,
+    'replace',
+    fromVersion,
+    toVersion
+  );
   return {
     updateType: 'rollback',
     branchName:
@@ -56,7 +56,7 @@ function getRollbackUpdate(config, versions) {
     commitMessageAction: 'Roll back',
     isRollback: true,
     newValue,
-    newMajor: getMajor(toVersion),
+    newMajor: version.getMajor(toVersion),
     semanticCommitType: 'fix',
   };
 }