Writing code that makes errors tougher – Donny Wals


As we work on initiatives, we normally add extra code than we take away. At the least that’s how issues are at first of our mission. Whereas our mission grows, the wants of the codebase change, and we begin refactoring issues. One factor that’s usually fairly onerous to get precisely proper when coding is the sorts of abstractions and design patterns we really want. On this submit, I wish to discover a mechanism that I prefer to leverage to verify my code is strong with out really worrying an excessive amount of about abstractions and design patterns within the first place.

We’ll begin off by sketching just a few situations wherein you would possibly end up questioning what to do. And even worse, situations the place you begin noticing that some issues go unsuitable typically, on some screens. After that, we’ll take a look at how we are able to leverage Swift’s sort system and entry management to forestall ourselves from writing code that’s liable to containing errors.

Widespread errors in codebases

While you take a look at codebases which have grown over time with out making use of the rules that I’d like to stipulate on this submit, you’ll usually see that the codebase accommodates code duplication, a number of if statements, some swap statements right here and there, and an entire bunch of mutable values.

None of those are errors on their very own, I might by no means, ever argue that the existence of an if assertion, swap, and even code duplication is a mistake that ought to instantly be rectified.

What I am saying is that these are sometimes signs of a codebase the place it turns into simpler and simpler over time to make errors. There’s a giant distinction there. The code itself won’t be the error; the code permits you as a developer to make errors extra simply when it’s not structured and designed to forestall errors.

Let’s check out some examples of how errors will be made too straightforward by means of code.

Errors because of code duplication

For instance, think about having a SwiftUI view that appears as follows:

struct MyView {
  @ObservedObject var viewModel: MyViewModel

  var physique: some View {
    Textual content("(viewModel.consumer.givenName) (viewModel.consumer.familyName) ((viewModel.consumer.e mail))")
  }
}

By itself, this doesn’t look too dangerous. We simply have a view, and a view mannequin, and to current one thing to the consumer we seize just a few view mannequin properties and we format them properly for our consumer.

As soon as the app that accommodates this view grows, we would must seize the identical knowledge from a (completely different) view mannequin, and format it equivalent to the way it’s formatted in different views.

Initially some copying and pasting will lower it however in some unspecified time in the future you’ll normally discover that issues get out of sync. One view presents knowledge a technique, and one other view presents knowledge in one other approach.

You can replace this view and consider mannequin as follows to repair the potential for errors:

class MyViewModel: ObservableObject {
  // ...

  var formattedUsername: String {
    return "(consumer.givenName) (consumer.familyName) ((consumer.e mail))"
  }
}
struct MyView {
  @ObservedObject var viewModel: MyViewModel

  var physique: some View {
    Textual content(viewModel.formattedUsername)
  }
}

With this code in place, we are able to use this view mannequin in a number of locations and reuse the formatted title.

It might be even higher if we moved the formatted title onto our Consumer object:

extension Consumer {
  // ...

  var formattedUsername: String {
    return "(givenName) (familyName) ((e mail))"
  }
}

struct MyView {
  @ObservedObject var viewModel: MyViewModel

  var physique: some View {
    Textual content(viewModel.consumer.formattedUsername)
  }
}

Whereas this code permits us to simply get a formatted username wherever we have now entry to a consumer, we’re violating a precept known as the Legislation of Demeter. I’ve written about this earlier than in a submit the place I speak about unfastened coupling so I gained’t go too in depth for now however the important thing level to recollect is that our view explicitly depends upon MyViewModel which is okay. Nonetheless, by accessing consumer.formattedUsername on this view mannequin, our view additionally has an implicit dependency on Consumer. And never simply that, it additionally depends upon view mannequin getting access to a consumer object.

I’d want to make yet one more change to this code and make it work as follows:

extension Consumer {
  // ...

  var formattedUsername: String {
    return "(givenName) (familyName) ((e mail))"
  }
}

class MyViewModel: ObservableObject {
  // ...

  var formattedUsername: String {
    return consumer.formattedUsername
  }
}
struct MyView {
  @ObservedObject var viewModel: MyViewModel

  var physique: some View {
    Textual content(viewModel.formattedUsername)
  }
}

This would possibly really feel a little bit redundant at first however when you begin being attentive to protecting your implicit dependencies in examine and also you attempt to solely entry properties on the item you depend upon with out chaining a number of accesses you’ll discover that making modifications to your code instantly requires much less work than it does when you might have implicit dependencies in all places.

One other type of code duplication can occur while you’re styling UI parts. For instance, you might need written some code that types a button in a selected approach.

If there’s a couple of place that ought to current this button, I may copy and paste it and issues will likely be wonderful.

Nonetheless, just a few months later we would must make the button labels daring as a substitute of normal font weight and will probably be approach too straightforward to overlook one or two buttons that we forgot about. We may do a full mission seek for Button however that might more than likely yield far more outcomes than simply the buttons that we need to change. This makes it far too straightforward to miss a number of buttons that we ought to be updating.

Duplicating code or logic a couple of times normally isn’t a giant deal. Actually, typically generalizing or putting the duplicated code someplace is extra tedious and complicated than it’s price. Nonetheless, when you begin to duplicate increasingly, or while you’re duplicating issues which might be important to maintain in sync, you need to think about making a small and light-weight abstraction or wrapper to forestall errors.

Stopping errors associated to code duplication

At any time when you end up reaching for cmd+c in your keyboard, you need to ask your self whether or not you’re about to repeat one thing that may have to be copied usually. Since none of us have the flexibility to reliably predict the long run, this may at all times be considerably of a guess. As you achieve extra expertise within the area you’ll develop a way for when issues are liable to duplication and a superb candidate to summary.

Particularly when an abstraction will be added in a easy method you shouldn’t have a really excessive tolerance for copying and pasting code.

Take into account the view mannequin instance from earlier. We had been in a position to resolve our downside by ensuring that we considered the precise degree of putting our consumer’s formatted title. Initially we put it on the view mannequin, however then we modified this by giving the consumer itself a formatted title. Permitting anyplace that has entry to our consumer object to seize a formatted title.

An additional advantage right here is we maintain our view mannequin as skinny as attainable, and we’ve made our consumer object extra versatile.

Within the case of a button that should seem in a number of locations it is sensible to wrap the button in a customized view. It may additionally make sense to write down a customized button fashion if that higher suits your use case.

Errors because of advanced state

Managing state is difficult. I don’t belief anyone that might argue in any other case.

It’s not unusual for code to slowly however absolutely flip into a posh state machine that makes use of a handful of boolean values and a few strings to find out what the app’s present state actually is. Typically the result’s that when as soon as boolean is true, a few others should be false as a result of this system can be in a nasty state in any other case.

My favourite instance of a scenario the place we have now a number of bits of state together with some guidelines about when this state is or isn’t legitimate is URLSession‘s callback for a knowledge activity:

URLSession.shared.dataTask(with: url) { knowledge, response, error in
  guard error == nil else {
    // one thing went unsuitable, deal with error
    return
  }

  guard let knowledge, let response else {
    // one thing went VERY unsuitable
    // we have now no error, no knowledge, and no response
    return
  }

  // use knowledge and response
}

If our request fails and comes again as an error, we all know that the response and knowledge arguments have to be nil and vice-versa. It is a easy instance however I’ve seen a lot worse in code I’ve labored on. And the issue was by no means launched knowingly. It’s at all times the results of slowly however absolutely rising the app and altering the necessities.

After we design our code, we are able to repair these sorts of issues earlier than they happen. While you discover that you could categorical an unimaginable state in your app as a result of a progress in variables which might be meant to work together collectively, think about leveraging enums to characterize the states your app will be in.

That approach, you considerably decrease your probabilities of writing incorrect state into your app, which your customers will take pleasure in.

For instance, Apple may have improved their URLSession instance with the Consequence sort for callbacks. Fortunately, with async / await dangerous state can’t be represented anymore as a result of a knowledge name now returns a non-optional Knowledge and URLResponse or throws an Error.

Errors because of not realizing the magical incantation

One final instance that I’d like to focus on is when codebases require you to name a collection of strategies in a selected order to be sure that all the pieces works accurately, and all bookkeeping is carried out accurately.

That is normally the results of API design that’s considerably missing in its usability.

One instance of that is the API for including and eradicating youngster view controllers in UIKit.

While you add a baby view controller you write code that appears a little bit like this:

addChild(childViewController)
// ... some setup code ...
childViewController.didMove(toParent: self)

That doesn’t appear too dangerous, proper.

The syntax for eradicating a baby view controller appears as follows:

childViewController.willMove(toParent: nil)
// ... some setup code ...
childViewController.removeFromParent()

The distinction right here is whether or not we name willMove or didMove on our childViewController. Not calling these strategies accurately can lead to too few or too many view controller lifecycle occasions being despatched to your youngster view controller. Personally, I at all times neglect whether or not I must name didMove or willMove after I work with youngster view controllers as a result of I do it too occasionally to recollect.

To repair this, the API design may very well be improved to mechanically name the proper methodology while you make a name to addChild or removeFromParent.

In your personal API design, you’ll need to look out for conditions the place your program solely works accurately while you name the precise strategies in the precise order. Particularly when the tactic calls ought to at all times be grouped intently collectively.

That stated, typically there’s a good cause why an API was designed the best way it was. I feel that is the case for Apple’s view controller containment APIs for instance. We’re alleged to arrange the kid view controller’s view between the calls we’re alleged to make. However nonetheless… the API may absolutely be reworked to make making errors tougher.

Designing code that helps stopping errors

While you’re writing code you need to at all times be looking out for anti-patterns like copy-pasting code lots, having a number of advanced state that enables for incorrect states to be represented, or while you’re writing code that has very particular necessities concerning the way it’s used.

As time goes on and also you achieve increasingly coding expertise, you’ll discover that it will get simpler and simpler to identify potential pitfalls, and you can begin getting forward of them by fixing issues earlier than they exist.

Normally which means you spent plenty of time eager about the way you need to name sure bits of code.

At any time when I’m engaged on a brand new characteristic, I have a tendency to write down my “name website” fist. The decision website means the half the place I work together with the characteristic code that I’m about to write down.

For instance, if I’m constructing a SwiftUI view that’s alleged to render an inventory of things which might be fetched from varied sources I’ll most likely write one thing like:

Checklist(itemSource.allItems) { merchandise in 
  // ...
}

After all, that code won’t work but however I’ll know what to purpose for. Regardless of what number of knowledge sources I find yourself with, I would like my Checklist to be straightforward to make use of.

This methodology of writing code by figuring out how I need to use it first will be utilized to each layer of your codebase. Generally it is going to work very well, different instances you’ll discover that it is advisable to deviate out of your “superb” name website however it helps deal with what issues; ensuring the code is simple to make use of.

At any time when I’m designing APIs I take into consideration this submit from Dave DeLong.

Specifically, this quote at all times stands out to me:

An amazing API is form to all builders who work with it.

Each methodology you write and each class you design has an API. And it’s a good suggestion to be sure that this API is pleasant to make use of. This contains ensuring that it’s onerous (or ideally, unimaginable) to misuse that API in addition to having good error messages and failure modes.

Shifting on from API design, when you’re modeling state that largely revolves round a number of booleans, think about enums as a substitute. Even when you’re modeling one thing like whether or not or not a view ought to animate, an enum may also help you make your code extra readable and maintainable in the long term.

Greater than something, when you assume {that a} sure little bit of code feels “off”, “too advanced” or “not fairly proper”, there’s a superb probability your instinct is right. Our code ought to be as simple to grasp as attainable. So at any time when we really feel like we’re doing the other, we must always right that.

That’s to not say that each one advanced code is dangerous. Or that each one repetition is dangerous. And even that each little bit of advanced state ought to grow to be an enum. These are all simply flags that ought to stand out to you as one thing that you need to take note of. Any time you’ll be able to change your code a bit in an effort to make it unimaginable to characterize an unimaginable state, or if you can also make some modifications to your code that guarantee you’ll be able to’t move dangerous arguments to a way, that’s a win.

In Abstract

Writing good code will be actually onerous. On this submit, I outlined a few examples of code that enables builders to make errors. There are numerous ways in which code can open a developer as much as errors, and these normally contain code that has developed over time, which may imply that blind spots have crept into the codebase with out the developer noticing.

By way of expertise, we are able to be taught to establish our blind spots early and we are able to defensively write code that anticipates change in a approach that ensures our code stays secure and simple to make use of.

General, state is the toughest factor to handle in my expertise. Modeling state in a approach that enables us to characterize advanced states in a secure method is extraordinarily helpful. Subsequent time you are contemplating writing an ‘if’ assertion that compares two or extra values to find out what ought to occur, think about writing an enum with a descriptive title and related values as a substitute.

What are some frequent coding errors that you’ve realized to establish alongside the best way? I’d love when you instructed me all about them on X or Threads.

Recent Articles

Related Stories

Leave A Reply

Please enter your comment!
Please enter your name here

Stay on op - Ge the daily news in your inbox