diff --git a/README.md b/README.md
index 2e9052b..1fbe9a4 100644
--- a/README.md
+++ b/README.md
@@ -140,6 +140,19 @@ DoubleDelay provides a simple function that doubles the duration passed in.
 This can then be easily used as the `BackoffFunc` in the `CallArgs`
 structure.
 
+## func ExpBackoff
+``` go
+func ExpBackoff(minDelay, maxDelay time.Duration, exp float64, applyJitter bool) func(time.Duration, int) time.Duration {
+```
+ExpBackoff returns a function a which generates time.Duration values using an
+exponential back-off algorithm with the specified parameters. The returned value
+can then be easily used as the `BackoffFunc` in the `CallArgs` structure.
+
+The next delay value is calculated using the following formula:
+  `newDelay = min(minDelay * exp^attempt, maxDelay)`
+
+If `applyJitter` is set to `true`, the function will randomly select and return
+back a value in the `[minDelay, newDelay]` range.
 
 ## func IsAttemptsExceeded
 ``` go
@@ -261,4 +274,4 @@ and Clock have been specified.
 
 
 - - -
-Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md)
\ No newline at end of file
+Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md)
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..427905f
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,17 @@
+module github.com/juju/retry
+
+go 1.15
+
+require (
+	github.com/juju/clock v0.0.0-20180524022203-d293bb356ca4
+	github.com/juju/errors v0.0.0-20150916125642-1b5e39b83d18
+	github.com/juju/loggo v0.0.0-20170605014607-8232ab8918d9 // indirect
+	github.com/juju/testing v0.0.0-20180807044555-c84dd6ba038a
+	github.com/juju/utils v0.0.0-20180424094159-2000ea4ff043 // indirect
+	github.com/juju/version v0.0.0-20161031051906-1f41e27e54f2 // indirect
+	golang.org/x/crypto v0.0.0-20180214000028-650f4a345ab4 // indirect
+	golang.org/x/net v0.0.0-20180406214816-61147c48b25b // indirect
+	gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2
+	gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4 // indirect
+	gopkg.in/yaml.v2 v2.0.0-20170712054546-1be3d31502d6 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..f6a21cc
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,22 @@
+github.com/juju/clock v0.0.0-20180524022203-d293bb356ca4 h1:v4AMWbdtZyIX8Ohv+FEpSwaCtho9uTtGbwjZab+rDuw=
+github.com/juju/clock v0.0.0-20180524022203-d293bb356ca4/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA=
+github.com/juju/errors v0.0.0-20150916125642-1b5e39b83d18 h1:Sem5Flzxj8ZdAgY2wfHBUlOYyP4wrpIfM8IZgANNGh8=
+github.com/juju/errors v0.0.0-20150916125642-1b5e39b83d18/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
+github.com/juju/loggo v0.0.0-20170605014607-8232ab8918d9 h1:Y+lzErDTURqeXqlqYi4YBYbDd7ycU74gW1ADt57/bgY=
+github.com/juju/loggo v0.0.0-20170605014607-8232ab8918d9/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
+github.com/juju/testing v0.0.0-20180807044555-c84dd6ba038a h1:dhnWDfRjO/h2XFBj/n3qm+wGjHm8/UqLSOk9GhZc+P0=
+github.com/juju/testing v0.0.0-20180807044555-c84dd6ba038a/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
+github.com/juju/utils v0.0.0-20180424094159-2000ea4ff043 h1:kjdsJcIYzmK2k4X2yVCi5Nip6sGoAuc7CLbp+qQnQUM=
+github.com/juju/utils v0.0.0-20180424094159-2000ea4ff043/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk=
+github.com/juju/version v0.0.0-20161031051906-1f41e27e54f2 h1:loQDi5MyxxNm7Q42mBGuPD6X+F6zw8j5S9yexLgn/BE=
+github.com/juju/version v0.0.0-20161031051906-1f41e27e54f2/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U=
+golang.org/x/crypto v0.0.0-20180214000028-650f4a345ab4 h1:OfaUle5HH9Y0obNU74mlOZ/Igdtwi3eGOKcljJsTnbw=
+golang.org/x/crypto v0.0.0-20180214000028-650f4a345ab4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/net v0.0.0-20180406214816-61147c48b25b h1:7rskAFQwNXGW6AD8E/6y0LDHW5mT9rsLD7ViLVFfh5w=
+golang.org/x/net v0.0.0-20180406214816-61147c48b25b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2 h1:+j1SppRob9bAgoYmsdW9NNBdKZfgYuWpqnYHv78Qt8w=
+gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4 h1:hILp2hNrRnYjZpmIbx70psAHbBSEcQ1NIzDcUbJ1b6g=
+gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
+gopkg.in/yaml.v2 v2.0.0-20170712054546-1be3d31502d6 h1:CvAnnm1XvMjfib69SZzDwgWfOk+PxYz0hA0HBupilBA=
+gopkg.in/yaml.v2 v2.0.0-20170712054546-1be3d31502d6/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
diff --git a/retry.go b/retry.go
index 777b5dc..1c20953 100644
--- a/retry.go
+++ b/retry.go
@@ -5,6 +5,8 @@ package retry
 
 import (
 	"fmt"
+	"math"
+	"math/rand"
 	"time"
 
 	"github.com/juju/errors"
@@ -223,3 +225,34 @@ func DoubleDelay(delay time.Duration, attempt int) time.Duration {
 	}
 	return delay * 2
 }
+
+// ExpBackoff returns a function a which generates time.Duration values using
+// an exponential back-off algorithm with the specified parameters. The
+// returned value can then be easily used as the BackoffFunc in the CallArgs
+// structure.
+//
+// The next delay value is calculated using the following formula:
+//   newDelay = min(minDelay * exp^attempt, maxDelay)
+//
+// If applyJitter is set to true, the function will randomly select and return
+// back a value in the [minDelay, newDelay] range.
+func ExpBackoff(minDelay, maxDelay time.Duration, exp float64, applyJitter bool) func(time.Duration, int) time.Duration {
+	minDelayF := float64(minDelay)
+	maxDelayF := float64(maxDelay)
+	return func(_ time.Duration, attempt int) time.Duration {
+		newDelay := minDelayF * math.Pow(exp, float64(attempt))
+		if newDelay > maxDelayF {
+			newDelay = maxDelayF
+		}
+
+		// Return a random value in the [minDelay, newDelay) range.
+		if applyJitter {
+			newDelay = rand.Float64() * newDelay
+			if newDelay < minDelayF {
+				newDelay = minDelayF
+			}
+		}
+
+		return time.Duration(newDelay).Round(time.Millisecond)
+	}
+}
diff --git a/retry_test.go b/retry_test.go
index c7ac3a6..4b8f62f 100644
--- a/retry_test.go
+++ b/retry_test.go
@@ -312,3 +312,43 @@ func (*retrySuite) TestMissingClockNotValid(c *gc.C) {
 	c.Check(err, jc.Satisfies, errors.IsNotValid)
 	c.Check(err, gc.ErrorMatches, `missing Clock not valid`)
 }
+
+type expBackoffSuite struct {
+	testing.LoggingSuite
+}
+
+var _ = gc.Suite(&expBackoffSuite{})
+
+func (*expBackoffSuite) TestExpBackoffWithoutJitter(c *gc.C) {
+	backoffFunc := retry.ExpBackoff(200*time.Millisecond, 2*time.Second, 2.0, false)
+	expDurations := []time.Duration{
+		200 * time.Millisecond,
+		400 * time.Millisecond,
+		800 * time.Millisecond,
+		1600 * time.Millisecond,
+		2000 * time.Millisecond, // capped to maxDelay
+	}
+
+	for attempt, expDuration := range expDurations {
+		got := backoffFunc(0, attempt)
+		c.Assert(got, gc.Equals, expDuration, gc.Commentf("unexpected duration for attempt %d", attempt))
+	}
+}
+
+func (*expBackoffSuite) TestExpBackoffWithtJitter(c *gc.C) {
+	minDelay := 200 * time.Millisecond
+	backoffFunc := retry.ExpBackoff(minDelay, 2*time.Second, 2.0, true)
+	maxDurations := []time.Duration{
+		200 * time.Millisecond,
+		400 * time.Millisecond,
+		800 * time.Millisecond,
+		1600 * time.Millisecond,
+		2000 * time.Millisecond, // capped to maxDelay
+	}
+
+	for attempt, maxDuration := range maxDurations {
+		got := backoffFunc(0, attempt)
+		c.Assert(got, jc.GreaterThan, minDelay-1, gc.Commentf("expected jittered duration for attempt %d to be in the [%s, %s] range; got %s", attempt, minDelay, maxDuration, got))
+		c.Assert(got, jc.LessThan, maxDuration+1, gc.Commentf("expected jittered duration for attempt %d to be in the [%s, %s] range; got %s", attempt, minDelay, maxDuration, got))
+	}
+}