start on simplifying
diff --git a/lib/adapter.js b/lib/adapter.js
index 322d221..fe8017b 100644
--- a/lib/adapter.js
+++ b/lib/adapter.js
@@ -2,6 +2,7 @@
 
 var utils = require('./utils');
 var pick = require('./deps/pick');
+var toPromise = require('./deps/toPromise');
 var collections = require('pouchdb-collections');
 var inherits = require('inherits');
 var getArguments = require('argsarray');
@@ -185,10 +186,6 @@
 
 AbstractPouchDB.prototype.post =
   adapterFun('post', function (doc, opts, callback) {
-  if (typeof opts === 'function') {
-    callback = opts;
-    opts = {};
-  }
   if (typeof doc !== 'object' || Array.isArray(doc)) {
     return callback(errors.error(errors.NOT_AN_OBJECT));
   }
@@ -196,32 +193,10 @@
 });
 
 AbstractPouchDB.prototype.put =
-  adapterFun('put', getArguments(function (args) {
-  var temp, temptype, opts, callback;
-  var doc = args.shift();
-  var id = '_id' in doc;
+  adapterFun('put', function(doc, opts, callback) {
   if (typeof doc !== 'object' || Array.isArray(doc)) {
-    callback = args.pop();
     return callback(errors.error(errors.NOT_AN_OBJECT));
   }
-  while (true) {
-    temp = args.shift();
-    temptype = typeof temp;
-    if (temptype === "string" && !id) {
-      doc._id = temp;
-      id = true;
-    } else if (temptype === "string" && id && !('_rev' in doc)) {
-      doc._rev = temp;
-    } else if (temptype === "object") {
-      opts = temp;
-    } else if (temptype === "function") {
-      callback = temp;
-    }
-    if (!args.length) {
-      break;
-    }
-  }
-  opts = opts || {};
   parseDoc.invalidIdError(doc._id);
   if (isLocalId(doc._id) && typeof this._putLocal === 'function') {
     if (doc._deleted) {
@@ -231,21 +206,15 @@
     }
   }
   this.bulkDocs({docs: [doc]}, opts, yankError(callback));
-}));
+});
 
 AbstractPouchDB.prototype.putAttachment =
   adapterFun('putAttachment', function (docId, attachmentId, rev,
-                                              blob, type, callback) {
+                                        blob, type, opts, callback) {
   var api = this;
-  if (typeof type === 'function') {
-    callback = type;
-    type = blob;
-    blob = rev;
-    rev = null;
-  }
-  // Lets fix in https://github.com/pouchdb/pouchdb/issues/3267
-  /* istanbul ignore if */
-  if (typeof type === 'undefined') {
+  if (typeof opts === 'function') {
+    callback = opts;
+    opts = type;
     type = blob;
     blob = rev;
     rev = null;
@@ -279,7 +248,7 @@
 
 AbstractPouchDB.prototype.removeAttachment =
   adapterFun('removeAttachment', function (docId, attachmentId, rev,
-                                                 callback) {
+                                           opts, callback) {
   var self = this;
   self.get(docId, function (err, obj) {
     /* istanbul ignore if */
@@ -312,20 +281,11 @@
       _id: docOrId,
       _rev: optsOrRev
     };
-    if (typeof opts === 'function') {
-      callback = opts;
-      opts = {};
-    }
   } else {
     // doc, opts, callback style
     doc = docOrId;
-    if (typeof optsOrRev === 'function') {
-      callback = optsOrRev;
-      opts = {};
-    } else {
-      callback = opts;
-      opts = optsOrRev;
-    }
+    callback = opts;
+    opts = optsOrRev;
   }
   opts = opts || {};
   opts.was_delete = true;
@@ -339,10 +299,6 @@
 
 AbstractPouchDB.prototype.revsDiff =
   adapterFun('revsDiff', function (req, opts, callback) {
-  if (typeof opts === 'function') {
-    callback = opts;
-    opts = {};
-  }
   var ids = Object.keys(req);
 
   if (!ids.length) {
@@ -422,14 +378,8 @@
 // compact one document and fire callback
 // by compacting we mean removing all revisions which
 // are further from the leaf in revision tree than max_height
-AbstractPouchDB.prototype.compactDocument =
-  adapterFun('compactDocument', function (docId, maxHeight, callback) {
-  var self = this;
-  this._getRevisionTree(docId, function (err, revTree) {
-    /* istanbul ignore if */
-    if (err) {
-      return callback(err);
-    }
+var compactDocument = toPromise(function(self, docId, maxHeight, callback) {
+  self._getRevisionTree(docId, function (err, revTree) {
     var height = computeHeight(revTree);
     var candidates = [];
     var revs = [];
@@ -453,13 +403,7 @@
 // compaction
 AbstractPouchDB.prototype.compact =
   adapterFun('compact', function (opts, callback) {
-  if (typeof opts === 'function') {
-    callback = opts;
-    opts = {};
-  }
-
   var self = this;
-  opts = opts || {};
 
   self._compactionQueue = self._compactionQueue || [];
   self._compactionQueue.push({opts: opts, callback: callback});
@@ -476,7 +420,7 @@
   var promises = [];
 
   function onChange(row) {
-    promises.push(self.compactDocument(row.id, 0));
+    promises.push(compactDocument(self, row.id, 0));
   }
   function onComplete(resp) {
     var lastSeq = resp.last_seq;
@@ -501,10 +445,6 @@
    _[method] */
 AbstractPouchDB.prototype.get =
   adapterFun('get', function (id, opts, callback) {
-  if (typeof opts === 'function') {
-    callback = opts;
-    opts = {};
-  }
   if (typeof id !== 'string') {
     return callback(errors.error(errors.INVALID_ID));
   }
@@ -661,10 +601,6 @@
   adapterFun('getAttachment', function (docId, attachmentId, opts,
                                               callback) {
   var self = this;
-  if (opts instanceof Function) {
-    callback = opts;
-    opts = {};
-  }
   this._get(docId, opts, function (err, res) {
     if (err) {
       return callback(err);
@@ -681,10 +617,6 @@
 
 AbstractPouchDB.prototype.allDocs =
   adapterFun('allDocs', function (opts, callback) {
-  if (typeof opts === 'function') {
-    callback = opts;
-    opts = {};
-  }
   opts.skip = typeof opts.skip !== 'undefined' ? opts.skip : 0;
   if (opts.start_key) {
     opts.startkey = opts.start_key;
@@ -724,12 +656,12 @@
 };
 
 AbstractPouchDB.prototype.close =
-  adapterFun('close', function (callback) {
+  adapterFun('close', function (opts, callback) {
   this._closed = true;
   return this._close(callback);
 });
 
-AbstractPouchDB.prototype.info = adapterFun('info', function (callback) {
+AbstractPouchDB.prototype.info = adapterFun('info', function (opts, callback) {
   var self = this;
   this._info(function (err, info) {
     if (err) {
@@ -743,7 +675,7 @@
   });
 });
 
-AbstractPouchDB.prototype.id = adapterFun('id', function (callback) {
+AbstractPouchDB.prototype.id = adapterFun('id', function (opts, callback) {
   return this._id(callback);
 });
 
@@ -754,13 +686,6 @@
 
 AbstractPouchDB.prototype.bulkDocs =
   adapterFun('bulkDocs', function (req, opts, callback) {
-  if (typeof opts === 'function') {
-    callback = opts;
-    opts = {};
-  }
-
-  opts = opts || {};
-
   if (Array.isArray(req)) {
     req = {
       docs: req
@@ -822,8 +747,8 @@
 
 AbstractPouchDB.prototype.registerDependentDatabase =
   adapterFun('registerDependentDatabase', function (dependentDb,
-                                                          callback) {
-  var depDB = new this.constructor(dependentDb, this.__opts);
+                                                    opts, callback) {
+    var depDB = new this.constructor(dependentDb, this.__opts);
 
   function diffFun(doc) {
     doc.dependentDbs = doc.dependentDbs || {};
@@ -841,12 +766,6 @@
 
 AbstractPouchDB.prototype.destroy =
   adapterFun('destroy', function (opts, callback) {
-
-  if (typeof opts === 'function') {
-    callback = opts;
-    opts = {};
-  }
-
   var self = this;
   var usePrefix = 'use_prefix' in self ? self.use_prefix : true;
 
diff --git a/lib/adapters/http/index.js b/lib/adapters/http/index.js
index e72f638..a3b8abe 100644
--- a/lib/adapters/http/index.js
+++ b/lib/adapters/http/index.js
@@ -230,7 +230,7 @@
     return 'http';
   };
 
-  api.id = adapterFun('id', function (callback) {
+  api.id = adapterFun('id', function (options, callback) {
     ajax({}, {method: 'GET', url: genUrl(host, '')}, function (err, result) {
       var uuid = (result && result.uuid) ?
         (result.uuid + host.db) : genDBUrl(host, '');
@@ -246,11 +246,6 @@
   // Sends a POST request to the host calling the couchdb _compact function
   //    version: The version of CouchDB it is running
   api.compact = adapterFun('compact', function (opts, callback) {
-    if (typeof opts === 'function') {
-      callback = opts;
-      opts = {};
-    }
-    opts = clone(opts);
     ajax(opts, {
       url: genDBUrl(host, '_compact'),
       method: 'POST'
@@ -379,12 +374,6 @@
   // The id could be solely the _id in the database, or it may be a
   // _design/ID or _local/ID path
   api.get = adapterFun('get', function (id, opts, callback) {
-    // If no options were given, set the callback to the second parameter
-    if (typeof opts === 'function') {
-      callback = opts;
-      opts = {};
-    }
-    opts = clone(opts);
 
     // List of parameters to add to the GET request
     var params = [];
@@ -503,20 +492,11 @@
         _id: docOrId,
         _rev: optsOrRev
       };
-      if (typeof opts === 'function') {
-        callback = opts;
-        opts = {};
-      }
     } else {
       // doc, opts, callback style
       doc = docOrId;
-      if (typeof optsOrRev === 'function') {
-        callback = optsOrRev;
-        opts = {};
-      } else {
-        callback = opts;
-        opts = optsOrRev;
-      }
+      callback = opts;
+      opts = optsOrRev;
     }
 
     var rev = (doc._rev || opts.rev);
@@ -536,10 +516,6 @@
   api.getAttachment =
     adapterFun('getAttachment', function (docId, attachmentId, opts,
                                                 callback) {
-    if (typeof opts === 'function') {
-      callback = opts;
-      opts = {};
-    }
     var params = opts.rev ? ('?rev=' + opts.rev) : '';
     var url = genDBUrl(host, encodeDocId(docId)) + '/' +
       encodeAttachmentId(attachmentId) + params;
@@ -553,12 +529,12 @@
   // Remove the attachment given by the id and rev
   api.removeAttachment =
     adapterFun('removeAttachment', function (docId, attachmentId, rev,
-                                                   callback) {
+                                             opts, callback) {
 
-    var url = genDBUrl(host, encodeDocId(docId) + '/' +
+      var url = genDBUrl(host, encodeDocId(docId) + '/' +
       encodeAttachmentId(attachmentId)) + '?rev=' + rev;
 
-    ajax({}, {
+    ajax(opts, {
       method: 'DELETE',
       url: url
     }, callback);
@@ -569,9 +545,10 @@
   // add it to the database given by host.
   api.putAttachment =
     adapterFun('putAttachment', function (docId, attachmentId, rev, blob,
-                                                type, callback) {
-    if (typeof type === 'function') {
-      callback = type;
+                                                type, opts, callback) {
+    if (typeof opts === 'function') {
+      callback = opts;
+      opts = type;
       type = blob;
       blob = rev;
       rev = null;
@@ -594,7 +571,7 @@
       blob = binary ? binStringToBluffer(binary, type) : '';
     }
 
-    var opts = {
+    opts = {
       headers: {'Content-Type': type},
       method: 'PUT',
       url: url,
@@ -608,36 +585,14 @@
 
   // Add the document given by doc (in JSON string format) to the database
   // given by host. This fails if the doc has no _id field.
-  api.put = adapterFun('put', getArguments(function (args) {
-    var temp, temptype, opts;
-    var doc = args.shift();
-    var callback = args.pop();
-
+  api.put = adapterFun('put', function(doc, opts, callback) {
     if (typeof doc !== 'object' || Array.isArray(doc)) {
       return callback(errors.error(errors.NOT_AN_OBJECT));
     }
 
-    var id = '_id' in doc;
     doc = clone(doc);
 
     preprocessAttachments(doc).then(function () {
-      while (true) {
-        temp = args.shift();
-        temptype = typeof temp;
-        if (temptype === "string" && !id) {
-          doc._id = temp;
-          id = true;
-        } else if (temptype === "string" && id && !('_rev' in doc)) {
-          doc._rev = temp;
-        } else if (temptype === "object") {
-          opts = clone(temp);
-        }
-        if (!args.length) {
-          break;
-        }
-      }
-      opts = opts || {};
-
       // check for any errors
       // TODO: rename this function
       parseDoc.invalidIdError(doc._id);
@@ -685,18 +640,12 @@
         callback(null, res);
       });
     }).catch(callback);
-  }));
+  });
 
   // Add the document given by doc (in JSON string format) to the database
   // given by host. This does not assume that doc is a new document
   // (i.e. does not have a _id or a _rev field.)
   api.post = adapterFun('post', function (doc, opts, callback) {
-    // If no options were given, set the callback to be the second parameter
-    if (typeof opts === 'function') {
-      callback = opts;
-      opts = {};
-    }
-    opts = clone(opts);
     if (typeof doc !== 'object') {
       return callback(errors.error(errors.NOT_AN_OBJECT));
     }
@@ -743,11 +692,6 @@
   // Get a listing of the documents in the database given
   // by host and ordered by increasing id.
   api.allDocs = adapterFun('allDocs', function (opts, callback) {
-    if (typeof opts === 'function') {
-      callback = opts;
-      opts = {};
-    }
-    opts = clone(opts);
     // List of parameters to add to the GET request
     var params = [];
     var body;
@@ -1071,12 +1015,6 @@
   // those that do NOT correspond to revisions stored in the database.
   // See http://wiki.apache.org/couchdb/HttpPostRevsDiff
   api.revsDiff = adapterFun('revsDiff', function (req, opts, callback) {
-    // If no options were given, set the callback to be the second parameter
-    if (typeof opts === 'function') {
-      callback = opts;
-      opts = {};
-    }
-
     // Get the missing document/revision IDs
     ajax(opts, {
       method: 'POST',
@@ -1089,7 +1027,7 @@
     callback();
   };
 
-  api._destroy = function (options, callback) {
+  api._destroy = adapterFun('destroy', function (options, callback) {
     setup().then(function() {
       ajax(options, {
         url: genDBUrl(host, ''),
@@ -1105,7 +1043,7 @@
         callback(null, resp);
       });
     }).catch(callback);
-  };
+  });
 }
 
 // HttpPouch is a valid adapter.
diff --git a/lib/deps/adapterFun.js b/lib/deps/adapterFun.js
index 63473da..d606574 100644
--- a/lib/deps/adapterFun.js
+++ b/lib/deps/adapterFun.js
@@ -3,37 +3,80 @@
 var Promise = require('./promise');
 var toPromise = require('./toPromise');
 var getArguments = require('argsarray');
+var errors = require('./errors');
+var parseDoc = require('./docs/parseDoc');
+var log = require('debug')('pouchdb:api');
 
-module.exports = function adapterFun(name, callback) {
-  var log = require('debug')('pouchdb:api');
-
-  function logApiCall(self, name, args) {
-    /* istanbul ignore if */
-    if (log.enabled) {
-      var logArgs = [self._db_name, name];
-      for (var i = 0; i < args.length - 1; i++) {
-        logArgs.push(args[i]);
-      }
-      log.apply(null, logArgs);
-
-      // override the callback itself to log the response
-      var origCallback = args[args.length - 1];
-      args[args.length - 1] = function (err, res) {
-        var responseArgs = [self._db_name, name];
-        responseArgs = responseArgs.concat(
-          err ? ['error', err] : ['success', res]
-        );
-        log.apply(null, responseArgs);
-        origCallback(err, res);
-      };
+function logApiCall(self, name, args) {
+  /* istanbul ignore if */
+  if (log.enabled) {
+    var logArgs = [self._db_name, name];
+    for (var i = 0; i < args.length - 1; i++) {
+      logArgs.push(args[i]);
     }
+    log.apply(null, logArgs);
+
+    // override the callback itself to log the response
+    var origCallback = args[args.length - 1];
+    args[args.length - 1] = function (err, res) {
+      var responseArgs = [self._db_name, name];
+      responseArgs = responseArgs.concat(
+        err ? ['error', err] : ['success', res]
+      );
+      log.apply(null, responseArgs);
+      origCallback(err, res);
+    };
+  }
+}
+
+function massagePutOrPostArgs(name, args) {
+  var temp, temptype, opts;
+  var doc = args.shift();
+  var callback = args.pop();
+  if (typeof doc !== 'object' || Array.isArray(doc)) {
+    return callback(errors.error(errors.NOT_AN_OBJECT));
   }
 
+  if (name === 'put') {
+    parseDoc.invalidIdError(doc._id);
+  }
+
+  var id = '_id' in doc;
+  while (true) {
+    temp = args.shift();
+    temptype = typeof temp;
+    if (temptype === "string" && !id) {
+      doc._id = temp;
+      id = true;
+    } else if (temptype === "string" && id && !('_rev' in doc)) {
+      doc._rev = temp;
+    } else if (temptype === "object") {
+      opts = temp;
+    }
+    if (!args.length) {
+      break;
+    }
+  }
+  return args;
+}
+
+function massageArgs(name, args) {
+  if (name === 'put' || name === 'post') {
+    return massagePutOrPostArgs(args);
+  }
+  if (typeof args[args.length - 2] !== 'object') {
+    args.splice(args.length - 1, 0, {});
+  }
+  return args;
+}
+
+module.exports = function adapterFun(name, callback) {
   return toPromise(getArguments(function (args) {
     if (this._closed) {
       return Promise.reject(new Error('database is closed'));
     }
     var self = this;
+    args = massageArgs(name, args);
     logApiCall(self, name, args);
     if (!this.taskqueue.isReady) {
       return new Promise(function (fulfill, reject) {