diff --git a/api/characteristics.go b/api/characteristics.go
index 3926b86..3d05002 100644
--- a/api/characteristics.go
+++ b/api/characteristics.go
@@ -132,15 +132,13 @@ func (c CharacteristicService) Update(id int64, e *types.Entity, genus string, c
 	payload.Characteristic.CanEdit = helpers.CanEdit(claims, payload.Characteristic.CreatedBy)
 
 	payload.Characteristic.CharacteristicTypeID = id
-	// TODO: fix this
-	count, err := models.DBH.Update(payload.Characteristic.CharacteristicBase)
-	if err != nil {
+
+	if err := models.Update(payload.Characteristic.CharacteristicBase); err != nil {
+		if err == errors.ErrCharacteristicNotUpdated {
+			return newJSONError(err, http.StatusBadRequest)
+		}
 		return newJSONError(err, http.StatusInternalServerError)
 	}
-	if count != 1 {
-		// TODO: fix this
-		return newJSONError(errors.ErrCharacteristicNotUpdated, http.StatusBadRequest)
-	}
 
 	strains, strainOpts, err := models.StrainsFromCharacteristicID(id, genus, claims)
 	if err != nil {
diff --git a/api/measurements.go b/api/measurements.go
index cfcff88..eb88bf5 100644
--- a/api/measurements.go
+++ b/api/measurements.go
@@ -95,15 +95,12 @@ func (m MeasurementService) Update(id int64, e *types.Entity, genus string, clai
 		payload.Measurement.TextMeasurementTypeID.Valid = true
 	}
 
-	// TODO: fix this
-	count, err := models.DBH.Update(payload.Measurement.MeasurementBase)
-	if err != nil {
+	if err := models.Update(payload.Measurement.MeasurementBase); err != nil {
+		if err == errors.ErrMeasurementNotUpdated {
+			return newJSONError(err, http.StatusBadRequest)
+		}
 		return newJSONError(err, http.StatusInternalServerError)
 	}
-	if count != 1 {
-		// TODO: fix this
-		return newJSONError(errors.ErrStrainNotUpdated, http.StatusBadRequest)
-	}
 
 	measurement, err := models.GetMeasurement(id, genus, claims)
 	if err != nil {
diff --git a/api/species.go b/api/species.go
index e80b5b5..72daf06 100644
--- a/api/species.go
+++ b/api/species.go
@@ -93,15 +93,12 @@ func (s SpeciesService) Update(id int64, e *types.Entity, genus string, claims *
 	}
 	payload.Species.SpeciesBase.GenusID = genusID
 
-	// TODO: fix this
-	count, err := models.DBH.Update(payload.Species.SpeciesBase)
-	if err != nil {
+	if err := models.Update(payload.Species.SpeciesBase); err != nil {
+		if err == errors.ErrSpeciesNotUpdated {
+			return newJSONError(err, http.StatusBadRequest)
+		}
 		return newJSONError(err, http.StatusInternalServerError)
 	}
-	if count != 1 {
-		// TODO: fix this
-		return newJSONError(errors.ErrSpeciesNotUpdated, http.StatusBadRequest)
-	}
 
 	// Reload to send back down the wire
 	species, err := models.GetSpecies(id, genus, claims)
diff --git a/api/strains.go b/api/strains.go
index 93d6d2f..7268f57 100644
--- a/api/strains.go
+++ b/api/strains.go
@@ -155,15 +155,12 @@ func (s StrainService) Update(id int64, e *types.Entity, genus string, claims *t
 	payload.Strain.UpdatedBy = claims.Sub
 	payload.Strain.ID = id
 
-	// TODO: fix this
-	count, err := models.DBH.Update(payload.Strain.StrainBase)
-	if err != nil {
+	if err := models.Update(payload.Strain.StrainBase); err != nil {
+		if err == errors.ErrStrainNotUpdated {
+			return newJSONError(err, http.StatusBadRequest)
+		}
 		return newJSONError(err, http.StatusInternalServerError)
 	}
-	if count != 1 {
-		// TODO: fix this
-		return newJSONError(errors.ErrStrainNotUpdated, http.StatusBadRequest)
-	}
 
 	strain, err := models.GetStrain(id, genus, claims)
 	if err != nil {
diff --git a/api/users.go b/api/users.go
index 309565d..2a20e43 100644
--- a/api/users.go
+++ b/api/users.go
@@ -109,15 +109,14 @@ func (u UserService) Update(id int64, e *types.Entity, dummy string, claims *typ
 		return &types.AppError{Error: err, Status: helpers.StatusUnprocessableEntity}
 	}
 
-	// TODO: fix this
-	count, err := models.DBH.Update(user.UserBase)
-	user.Password = ""
-	if err != nil {
+	if err := models.Update(user.UserBase); err != nil {
+		if err == errors.ErrUserNotUpdated {
+			return newJSONError(err, http.StatusBadRequest)
+		}
 		return newJSONError(err, http.StatusInternalServerError)
 	}
-	if count != 1 {
-		return newJSONError(errors.ErrUserNotUpdated, http.StatusInternalServerError)
-	}
+
+	user.Password = ""
 
 	return nil
 }
diff --git a/errors/measurements.go b/errors/measurements.go
index 1a65f4a..63b7d22 100644
--- a/errors/measurements.go
+++ b/errors/measurements.go
@@ -5,4 +5,6 @@ import "errors"
 var (
 	// ErrMeasurementNotFound when not found.
 	ErrMeasurementNotFound = errors.New("Measurement not found")
+	// ErrMeasurementNotUpdate when not updated.
+	ErrMeasurementNotUpdated = errors.New("Measurement not updated")
 )
diff --git a/models/characteristics.go b/models/characteristics.go
index cb56ecc..fec69e5 100644
--- a/models/characteristics.go
+++ b/models/characteristics.go
@@ -28,6 +28,10 @@ func (c *CharacteristicBase) PreUpdate(e modl.SqlExecutor) error {
 	return nil
 }
 
+func (c *CharacteristicBase) UpdateError() error {
+	return errors.ErrCharacteristicNotUpdated
+}
+
 // CharacteristicBase is what the DB expects for write operations
 type CharacteristicBase struct {
 	ID                   int64           `json:"id,omitempty"`
diff --git a/models/interfaces.go b/models/interfaces.go
new file mode 100644
index 0000000..723ae67
--- /dev/null
+++ b/models/interfaces.go
@@ -0,0 +1,19 @@
+package models
+
+import "github.com/thermokarst/bactdb/Godeps/_workspace/src/github.com/jmoiron/modl"
+
+type updater interface {
+	PreUpdate(modl.SqlExecutor) error
+	UpdateError() error
+}
+
+func Update(u updater) error {
+	count, err := DBH.Update(u)
+	if err != nil {
+		return err
+	}
+	if count != 1 {
+		return u.UpdateError()
+	}
+	return nil
+}
diff --git a/models/measurements.go b/models/measurements.go
index 3ccc945..b10eba1 100644
--- a/models/measurements.go
+++ b/models/measurements.go
@@ -29,6 +29,10 @@ func (m *MeasurementBase) PreUpdate(e modl.SqlExecutor) error {
 	return nil
 }
 
+func (m *MeasurementBase) UpdateError() error {
+	return errors.ErrMeasurementNotUpdated
+}
+
 // MeasurementBase is what the DB expects for write operations
 // There are three types of supported measurements: fixed-text, free-text,
 // & numerical. The table has a constraint that will allow at most one
diff --git a/models/species.go b/models/species.go
index 1992e49..3152995 100644
--- a/models/species.go
+++ b/models/species.go
@@ -29,6 +29,10 @@ func (s *SpeciesBase) PreUpdate(e modl.SqlExecutor) error {
 	return nil
 }
 
+func (s *SpeciesBase) UpdateError() error {
+	return errors.ErrSpeciesNotUpdated
+}
+
 // SpeciesBase is what the DB expects for write operations.
 type SpeciesBase struct {
 	ID                  int64            `db:"id" json:"id"`
diff --git a/models/strains.go b/models/strains.go
index b314d39..9f98383 100644
--- a/models/strains.go
+++ b/models/strains.go
@@ -29,6 +29,10 @@ func (s *StrainBase) PreUpdate(e modl.SqlExecutor) error {
 	return nil
 }
 
+func (s *StrainBase) UpdateError() error {
+	return errors.ErrStrainNotUpdated
+}
+
 // StrainBase is what the DB expects for write operations.
 type StrainBase struct {
 	ID                  int64            `db:"id" json:"id"`
diff --git a/models/users.go b/models/users.go
index 9b04f96..9306bb2 100644
--- a/models/users.go
+++ b/models/users.go
@@ -5,6 +5,7 @@ import (
 	"encoding/json"
 	"regexp"
 
+	"github.com/thermokarst/bactdb/Godeps/_workspace/src/github.com/jmoiron/modl"
 	"github.com/thermokarst/bactdb/Godeps/_workspace/src/golang.org/x/crypto/bcrypt"
 	"github.com/thermokarst/bactdb/errors"
 	"github.com/thermokarst/bactdb/helpers"
@@ -15,6 +16,24 @@ func init() {
 	DB.AddTableWithName(UserBase{}, "users").SetKeys(true, "ID")
 }
 
+// PreInsert is a modl hook.
+func (u *UserBase) PreInsert(e modl.SqlExecutor) error {
+	ct := helpers.CurrentTime()
+	u.CreatedAt = ct
+	u.UpdatedAt = ct
+	return nil
+}
+
+// PreUpdate is a modl hook.
+func (u *UserBase) PreUpdate(e modl.SqlExecutor) error {
+	u.UpdatedAt = helpers.CurrentTime()
+	return nil
+}
+
+func (u *UserBase) UpdateError() error {
+	return errors.ErrUserNotUpdated
+}
+
 // UserBase is what the DB expects to see for write operations.
 type UserBase struct {
 	ID        int64          `json:"id,omitempty"`