Self-documenting code to improve readability in Swift

Self-documenting code can improve the readability of your codebase. It basically means that you don’t have to write comments to explain what your code is doing. Its advantages can be listed as followed

  • Improved readability of your codebase
  • Code is easier to understand
  • Forces to think of single responsibility

With a few basic tips, it’s quite easy to write self-documenting code by default. Even if you decide to use documentation in your code, it’s good to write describing and readable class, method and parameter names. Let’s go over the tips.

Focus on single responsibility

Start with writing code by keeping single responsibility as your focus. If your class or method contains many different responsibilities, it’s probably very hard to write a good method name.

func downloadImageAndApplyFilterAfterCroppingAndPresentGallery()

This is obviously an extreme example. However, we could use this to explain single responsibility and show you what the difference could be if we would split this method into multiple ones.

func userDidSubmitImage() {
    downloadImage()
    cropImage()
    applyFilterToImage()
    presentGallery()
}

As each method is on its own, we automatically create a more readable enclosing method. It’s also a lot easier to understand the code as every single method is now responsible for only one thing.

Use extensions on existing types

Extensions on existing types can immediately improve your readability and explain your code.

  • It’s clear on which object a method is performed
  • Code is easier to re-use throughout the codebase
  • Keeps your classes small by moving logic into a file containing the extension

The example showed earlier contained method names in which we had to name “image” all the time: downloadImage, cropImage, applyFilterToImage. This wouldn’t be needed if we would’ve written an extension to the image type.

extension UIImage {
    func cropped() -> UIImage { ... }
    func applyFilter() -> UIImage { ... }
}

func userDidSubmitImage() {
    let image = downloadImage()
        .cropped()
        .applyFilter()
    presentGallery()
}

This makes it clear that we’re modifying the image we’ve just downloaded. In some cases, code readability could be even more improved by combining extensions with type aliases. All together it makes your code a lot more self-documenting.

Stop using trailing closures

Trailing closures can be used when a function’s final argument is a closure expression. It allows you to skip the argument label for the closure as part of the function call.

/// Including the argument labbel:
viewController.dismiss(animated: true, completion: {
})

/// Skipping the argument label:
viewController.dismiss(animated: true) {
}

Although in most of these cases you probably know it’s a completion callback, it does not necessarily have to be the case. And even in those cases, it could be that your fellow colleague who just started as a developer does not know yet what is going on. In that case, they need to go to the method declaration to find out for themselves what is going on.

Think about it when you implement a method containing a trailing closure and ask yourself whether it’s clear when the closure is called.

Stop using a type alias for a closure

It’s quite common to write a type alias for a closure.

typealias CompletionCallback = (_ result: Result<String, Error>) -> Void

func download(_ url: URL, completion: CompletionCallback) {
    // ...
}

However, when navigating through the methods, it’s hard to tell what is in the CompletionCallback closure. Therefore, it’s better to write out the closure, even if it’s used in many places. If your closure is quite complex and it, therefore, feels better to use the type alias instead, you might want to rethink your methods responsibility to see whether it’s not doing more than it should.

Naming is hard

Naming is hard and we all know it. How many “Managers” do you have in your codebase?

Unfortunately, I don’t have the golden bullet to solve this problem. What helps for me is to explicitly write what a class is doing, combined with single responsibility. Instead of DownloadManager I would call it a Downloader, for example. Use semantic naming and try to describe exactly what a method is doing.

Should I stop writing documentation and comments?

No, definitely not! Self-documenting code just helps other developers to understand your codebase. It does not mean that there shouldn’t be any comments or documentation, but only necessary ones. Writing a lot of documentation like “Downloads the image” for a method called downloadImage() only makes your code harder to read.

Therefore, start by writing self-documenting code and see whether any extra explanation is needed. Don’t just comment or document for the sake of documenting every single method or class.

Conclusion

Naming is hard and self-documenting code is not enough reason to stop writing documentation at all. The end goal should be to make your code easy to understand with as less clutter as possible.

Thanks for reading!