In the early days of the web, circa 1995 or so, it was common for the forms on websites to return a single error on submission, identifying only the first field that needed attention. This resulted frequent failed submissions as users would only be able to fix one thing at a time. To say that this made for a laborious and frustrating process would be a massive understatement.

Some of those websites would even return the user to a blank webform, requiring everything to be re-entered from scratch!

Often, users would only persist with completion the form if they had no alternative – which must have resulted in costly abandoned sales for the ecommerce websites of the era.

User experiences became significantly better – and the volume of ecommerce sales increased dramatically – once forms started identifying all the outstanding issues on form submission, allowing users to fix them all in one pass.

We can apply the same lessons to our software by aggregating errors where appropriate and returning them in one go. In the Azure Service Operator (ASO) code generator we applied this when processing multiple items in a loop.

While one failure in the loop was enough to cause the code generator to fail with a fatal error, we processed all the items in the loop, returning an aggregate of those errors at the end to allow all of the issues to be addressed at one time.

The required code is remarkably simple; we followed a standard pattern using the Kubernetes errors package, conventially imported as kerrors:

var errs []error
for _, def := range defs {

    t, err := // elided
    if err != nil {
        errs = append(errs, err)
        continue
    }

    // elided
}

if len(errs) > 0 {
    return nil, kerrors.NewAggregate(errs)
}

While the NewAggregate() factory has the useful bonus feature of returning a nil if the passed slice is empty (see the implementation), we usually made an explicit check to ensure we return an explicit nil instead of an invalid result.

By returning all the errors in one go, we enable our users to take remedial action so their next invocation of the generator will successfully execute that stage of processing and (hopefully!) generate the code required for their new custom resource definition.

Taking a few minutes to improve the developer experience (DX) of someone using your code is a worthwhile investment of your time - after all, you’re going to benefit as well.

Comments

blog comments powered by Disqus