diff --git a/lib/config/definitions.js b/lib/config/definitions.js
index aa9ce9ceecaadc67da37adc7d84e19267da14465..deadf948b8ffa2eb4b9bb2670a7b6774d5e3a403 100644
--- a/lib/config/definitions.js
+++ b/lib/config/definitions.js
@@ -405,6 +405,16 @@ const options = [
     stage: 'package',
     type: 'boolean',
   },
+  {
+    name: 'versionStrategy',
+    description:
+      'Strategy for how to modify/update existing versions/semver. Possible values: auto, replace, or widen',
+    stage: 'package',
+    type: 'string',
+    default: 'auto',
+    cli: false,
+    env: false,
+  },
   {
     name: 'branchPrefix',
     description: 'Prefix to use for all branch names',
diff --git a/lib/workers/package/versions.js b/lib/workers/package/versions.js
index 5c482a08f70f7fd1f87d980f9006149389fdc221..04f77db3817a48280f4756053211cf89c79e5a82 100644
--- a/lib/workers/package/versions.js
+++ b/lib/workers/package/versions.js
@@ -199,7 +199,15 @@ function determineUpgrades(npmDep, config) {
     .map(upgrade => ({ ...upgrade, ...{ isRange: true } }))
     .map(upgrade => {
       const { major, minor } = semverUtils.parse(upgrade.newVersion);
-      if (lastSemver.operator === '~' && semverParsed.length === 1) {
+      const canReplace = config.versionStrategy !== 'widen';
+      const forceReplace = config.versionStrategy === 'replace';
+      const canWiden = config.versionStrategy !== 'replace';
+      const forceWiden = config.versionStrategy === 'widen';
+      if (
+        lastSemver.operator === '~' &&
+        canReplace &&
+        (semverParsed.length === 1 || forceReplace)
+      ) {
         // Utilise that a.b is the same as ~a.b.0
         const minSatisfying = semver.minSatisfying(
           versionList,
@@ -207,19 +215,27 @@ function determineUpgrades(npmDep, config) {
         );
         // Add a tilde before that version number
         return { ...upgrade, ...{ newVersion: `~${minSatisfying}` } };
-      } else if (lastSemver.operator === '~' && semverParsed.length > 1) {
+      } else if (
+        lastSemver.operator === '~' &&
+        canWiden &&
+        (semverParsed.length > 1 || forceWiden)
+      ) {
         // Utilise that a.b is the same as ~a.b.0
         const minSatisfying = semver.minSatisfying(
           versionList,
           `${major}.${minor}`
         );
         // Add a tilde before that version number
-        const newVersion = `~${minSatisfying}`;
+        const newVersion = `${currentVersion} || ~${minSatisfying}`;
         return {
           ...upgrade,
-          newVersion: currentVersion + ' || ' + newVersion,
+          newVersion,
         };
-      } else if (lastSemver.operator === '^' && semverParsed.length === 1) {
+      } else if (
+        lastSemver.operator === '^' &&
+        canReplace &&
+        (semverParsed.length === 1 || forceReplace)
+      ) {
         let newVersion;
         // Special case where major and minor are 0
         if (major === '0' && minor === '0') {
@@ -232,7 +248,11 @@ function determineUpgrades(npmDep, config) {
           newVersion = `^${minSatisfying}`;
         }
         return { ...upgrade, newVersion };
-      } else if (lastSemver.operator === '^' && semverParsed.length > 1) {
+      } else if (
+        lastSemver.operator === '^' &&
+        canWiden &&
+        (semverParsed.length > 1 || forceWiden)
+      ) {
         // If version is < 1, then semver treats ^ same as ~
         const newRange = major === '0' ? `${major}.${minor}` : `${major}`;
         const minSatisfying = semver.minSatisfying(versionList, newRange);
diff --git a/test/workers/package/__snapshots__/versions.spec.js.snap b/test/workers/package/__snapshots__/versions.spec.js.snap
index dc234de8c66d0c7e0857826d384a28166f2cc6aa..7c3ebcf4403c0dd6a40f12499c7a0475a4ba5292 100644
--- a/test/workers/package/__snapshots__/versions.spec.js.snap
+++ b/test/workers/package/__snapshots__/versions.spec.js.snap
@@ -80,6 +80,38 @@ Array [
 ]
 `;
 
+exports[`workers/package/versions .determineUpgrades(npmDep, config) replaces major complex ranged versions if configured 1`] = `
+Array [
+  Object {
+    "changeLogFromVersion": "2.7.0",
+    "changeLogToVersion": "3.8.1",
+    "isMajor": true,
+    "isRange": true,
+    "newVersion": "^3.0.0",
+    "newVersionMajor": 3,
+    "newVersionMinor": 8,
+    "type": "major",
+    "unpublishable": false,
+  },
+]
+`;
+
+exports[`workers/package/versions .determineUpgrades(npmDep, config) replaces minor complex ranged versions if configured 1`] = `
+Array [
+  Object {
+    "changeLogFromVersion": "1.3.0",
+    "changeLogToVersion": "1.4.1",
+    "isMinor": true,
+    "isRange": true,
+    "newVersion": "~1.4.0",
+    "newVersionMajor": 1,
+    "newVersionMinor": 4,
+    "type": "minor",
+    "unpublishable": false,
+  },
+]
+`;
+
 exports[`workers/package/versions .determineUpgrades(npmDep, config) return warning if empty versions 1`] = `
 Object {
   "message": "No versions returned from registry for this package",
@@ -857,6 +889,38 @@ Array [
 ]
 `;
 
+exports[`workers/package/versions .determineUpgrades(npmDep, config) widens major ranged versions if configured 1`] = `
+Array [
+  Object {
+    "changeLogFromVersion": "2.7.0",
+    "changeLogToVersion": "3.8.1",
+    "isMajor": true,
+    "isRange": true,
+    "newVersion": "^2.0.0 || ^3.0.0",
+    "newVersionMajor": 3,
+    "newVersionMinor": 8,
+    "type": "major",
+    "unpublishable": false,
+  },
+]
+`;
+
+exports[`workers/package/versions .determineUpgrades(npmDep, config) widens minor ranged versions if configured 1`] = `
+Array [
+  Object {
+    "changeLogFromVersion": "1.3.0",
+    "changeLogToVersion": "1.4.1",
+    "isMinor": true,
+    "isRange": true,
+    "newVersion": "~1.3.0 || ~1.4.0",
+    "newVersionMajor": 1,
+    "newVersionMinor": 4,
+    "type": "minor",
+    "unpublishable": false,
+  },
+]
+`;
+
 exports[`workers/package/versions .determineUpgrades(npmDep, config) widens stanndalone major OR ranges 1`] = `
 Array [
   Object {
diff --git a/test/workers/package/versions.spec.js b/test/workers/package/versions.spec.js
index 6afb2e66e22efde958f8a05986f72c0c0e9bcd70..c9d9a3df4320617f5ccd349c9157615df47f20df 100644
--- a/test/workers/package/versions.spec.js
+++ b/test/workers/package/versions.spec.js
@@ -8,7 +8,7 @@ let config;
 
 describe('workers/package/versions', () => {
   beforeEach(() => {
-    config = require('../../../lib/config/defaults').getConfig();
+    config = { ...require('../../../lib/config/defaults').getConfig() };
     config.pinVersions = true;
   });
 
@@ -104,6 +104,30 @@ describe('workers/package/versions', () => {
       config.currentVersion = '~1.0.0';
       expect(versions.determineUpgrades(qJson, config)).toMatchSnapshot();
     });
+    it('widens minor ranged versions if configured', () => {
+      config.pinVersions = false;
+      config.currentVersion = '~1.3.0';
+      config.versionStrategy = 'widen';
+      expect(versions.determineUpgrades(qJson, config)).toMatchSnapshot();
+    });
+    it('replaces minor complex ranged versions if configured', () => {
+      config.pinVersions = false;
+      config.currentVersion = '~1.2.0 || ~1.3.0';
+      config.versionStrategy = 'replace';
+      expect(versions.determineUpgrades(qJson, config)).toMatchSnapshot();
+    });
+    it('widens major ranged versions if configured', () => {
+      config.pinVersions = false;
+      config.currentVersion = '^2.0.0';
+      config.versionStrategy = 'widen';
+      expect(versions.determineUpgrades(webpackJson, config)).toMatchSnapshot();
+    });
+    it('replaces major complex ranged versions if configured', () => {
+      config.pinVersions = false;
+      config.currentVersion = '^1.0.0 || ^2.0.0';
+      config.versionStrategy = 'replace';
+      expect(versions.determineUpgrades(webpackJson, config)).toMatchSnapshot();
+    });
     it('pins minor ranged versions', () => {
       config.currentVersion = '^1.0.0';
       expect(versions.determineUpgrades(qJson, config)).toMatchSnapshot();
diff --git a/website/docs/_posts/2017-10-05-configuration-options.md b/website/docs/_posts/2017-10-05-configuration-options.md
index 3abd6bf4ceb4b667bd5df2f690f09fbe0c32ce00..4db8319de3e800f8e219de57b3a7a8ecdccbdd3c 100644
--- a/website/docs/_posts/2017-10-05-configuration-options.md
+++ b/website/docs/_posts/2017-10-05-configuration-options.md
@@ -1004,6 +1004,26 @@ When schedules are in use, it generally means "no updates". However there are ca
 
 This is default true, meaning that Renovate will perform certain "desirable" updates to _existing_ PRs even when outside of schedule. If you wish to disable all updates outside of scheduled hours then set this field to false.
 
+## versionStrategy
+
+Strategy for how to modify/update existing versions/semver. Possible values: auto, replace, or widen
+
+| name    | value  |
+| ------- | ------ |
+| type    | string |
+| default | 'auto' |
+
+npm-only.
+
+Renovate's "auto" strategy for updating versions is like this:
+
+1. If the existing version already ends with an "or" operator - e.g. `"^1.0.0 || ^2.0.0"` - then Renovate will widen it, e.g. making it into `"^1.0.0 || ^2.0.0 || ^3.0.0"`.
+2. Otherwise, replace it. e.g. `"^2.0.0"` would be replaced by `"^3.0.0"`
+
+You can override logic either way, by setting it to `replace` or `widen`. e.g. if the currentVersion is `"^1.0.0 || ^2.0.0"` but you configure `versionStrategy=replace` then the result will be `"^3.0.0"`.
+
+Or for example if you configure all `peerDependencies` with `versionStrategy=widen` and have `"react": "^15.0.0"` as current version then it will be updated to `"react": "^15.0.0 || ^16.0.0"`.
+
 ## yarnrc
 
 A string copy of yarnrc file.