Computer scienceProgramming languagesKotlinAdditional instrumentsAdditional code toolsCode Documentation

Code Documentation

6 minutes read

Documenting code is a fundamental task as a developer. It's not a trivial or meaningless task. Documenting helps keep our code understandable over time and can be used not only by ourselves, but also by other developers.

In this topic, we'll cover the basics of documenting code with KDoc and Dokka.

Kdoc

KDoc is the documentation generation tool for Kotlin, the statically-typed programming language from JetBrains. It's similar to Java's Javadoc tool, generating API documentation from comments in the source code.

In Kotlin, you can write KDoc comments directly above the declarations of classes, methods, or functions that you want to document. The comment should start with /** and end with */. Inside the comment, you can use Markdown for formatting.

Here's an example:

/**
 * This is a KDoc comment for the following function. It explains what the function does.
 *
 * @param x The first parameter.
 * @param y The second parameter.
 * @return The sum of x and y.
 */
fun add(x: Int, y: Int): Int {
    return x + y
}

In this example, the KDoc comment explains what the add function does, and describes its parameters and return value. The @param and @return tags are used to provide specific details about the function's parameters and return value.

When you generate the documentation (for example, by using the Dokka tool), this comment will be included in the output, providing a description of the add function to anyone who reads the documentation.

Block tags

KDoc, the documentation generator for Kotlin, allows developers to write comprehensive descriptions and explanations for their code. It uses a specific syntax for various elements, such as parameters, return values, constructors, and more. Here's a brief explanation of some common KDoc block tags:

  • @param name: This tag is used to explain the purpose of a function's or a type parameter's value. The parameter's name can optionally be enclosed in brackets for clarity, like `@param[name]`.
  • @return: This tag is used to describe what a function returns.
  • @constructor: This tag documents the primary constructor of a class.
  • @receiver: This tag is used to document the receiver of an extension function.
  • @property name: This tag is used to document a class property with the given name. It's particularly useful for properties declared in the primary constructor where a direct doc comment might be awkward.
  • @throws class, @exception class: These tags are used to document exceptions that a method might throw.
  • @sample identifier: This tag embeds a function as an example in the current element's documentation.
  • @see identifier: This tag adds a link to the specified class or method in the "See also" section of the documentation.
  • @author: This tag specifies the author of the documented element.
  • @since: This tag specifies the version in which the documented element was introduced.
  • @suppress: This tag excludes the element from the generated documentation. It can be used for elements not part of the official API of a module but still need to be externally visible.
  • @Deprecated: This tag is used to indicated this method is deprecated.
/**
 * This is a KDoc comment for the Person class. It describes the class and its behavior.
 *
 * @property name The name of the person.
 * @property age The age of the person.
 * @constructor Creates a new instance of the Person class.
 * @author John Doe
 * @since 1.0
 */
class Person(val name: String, var age: Int) {
    /**
     * A method to celebrate a person's birthday. It increases the person's age by 1.
     * 
     * @return A birthday message.
     * @throws Exception if the person's age is negative.
     * @sample celebrateBirthdaySample
     */
    fun celebrateBirthday(): String {
        if (age < 0) {
            throw Exception("Age cannot be negative")
        }
        age++
        return "Happy birthday $name, you are now $age years old!"
    }

    /**
     * A method to introduce the person.
     * 
     * @return An introduction message.
     * @see Person.name
     * @see Person.age
     */
    fun introduce(): String {
        return "Hello, my name is $name and I am $age years old."
    }
}

/**
 * A sample usage of the celebrateBirthday method.
 * 
 * @suppress
 */
fun celebrateBirthdaySample() {
    val john = Person("John", 20)
    println(john.celebrateBirthday())
}

In this example, the Person class has two properties, name and age, which are documented using the @property tag. The celebrateBirthday method is documented with the @return and @throws tags, and a sample usage of it is provided with the @sample tag. The introduce method is documented with the @return and @see tags. The @author and @since tags are used at the class level to provide additional information. The celebrateBirthdaySample function is suppressed from the generated documentation with the @suppress tag.

Using Markdown

For inline markup, KDoc employs standard Markdown syntax with extensions for linking to other code elements.

  • Links to Code Elements: To create a link to another code element such as a class, method, property, or parameter, you simply enclose the name of the element in square brackets. For example:
/**
 * Use the method [celebrateBirthday] for this purpose.
 */
  • If you want to use a custom label for the link, you can add it in another set of square brackets before the element link:
/**
 * Use [this method][celebrateBirthday] for this purpose.
 */
  • You can also use fully qualified names in the element links. Note that, unlike Javadoc, qualified names always use the dot character to separate the components, even before a method name:
/**
 * Use [Person.celebrateBirthday] to celebrate the person's birthday.
 */
  • External Links: To add an external link, use the typical Markdown syntax:
/**
 * For more information about KDoc syntax, see [KDoc](https://kotlinlang.org/docs/kotlin-doc.html).
 */

Now, let's incorporate these concepts into the Person class example:

/**
 * This is a [Person] class. It represents a person with a [name] and an [age].
 * 
 * @property name The name of the person.
 * @property age The age of the person.
 * @constructor Creates a new instance of the [Person] class.
 * @author John Doe
 * @since 1.0
 */
class Person(val name: String, var age: Int) {
    /**
     * A method to celebrate a person's birthday. It increases the person's [age] by 1.
     * 
     * @return A birthday message.
     * @throws Exception if the person's age is negative.
     * @sample celebrateBirthdaySample
     * @see age
     */
    fun celebrateBirthday(): String {
        if (age < 0) {
            throw Exception("Age cannot be negative")
        }
        age++
        return "Happy birthday $name, you are now $age years old!"
    }

    /**
     * A method to introduce the person.
     * 
     * @return An introduction message.
     * @see name
     * @see age
     */
    fun introduce(): String {
        return "Hello, my name is $name and I am $age years old."
    }
}

/**
 * A sample usage of the [Person.celebrateBirthday] method.
 * 
 * @suppress
 */
fun celebrateBirthdaySample() {
    val john = Person("John", 20)
    println(john.celebrateBirthday())
}

In this example, the KDoc comments include links to the Person class, the name and age properties, and the celebrateBirthday method. The external link syntax is not used here, but it could be used to link to external resources for additional information.

Dokka

Dokka is a tool designed for generating API documentation for Kotlin and mixed-language projects. It recognizes both Kotlin's KDoc and Java's Javadoc comments, making it versatile and useful for various projects.

The tool is capable of generating documentation in a variety of formats, including Dokka HTML style, various styles of Markdown, and Javadoc HTML. This flexibility allows developers to choose the format that best suits their needs.

Several well-known libraries use Dokka for their API documentation, including kotlinx.coroutines, Bitmovin, Hexagon, Ktor, and OkHttp. This highlights the widespread acceptance and utility of Dokka in the Kotlin community.

Dokka is highly adaptable and can be run using different methods such as Gradle, Maven, or directly from the command line.

You can install Dokka using its Gradle plugin

plugins {
    id("org.jetbrains.dokka") version "1.9.10"
}

To generate documentation, run the following Gradle tasks:

  • dokkaHtml for single-project builds

  • dokkaHtmlMultiModule for multi-project builds

By default, the output directory is set to /build/dokka/html and /build/dokka/htmlMultiModule.

Generate Documentation

The Gradle plugin for Dokka comes with built-in HTML, Markdown, and Javadoc output formats, adding numerous tasks for generating documentation for both single and multi-project builds.

Single-project builds

For simple, single-project applications and libraries, use the following tasks to build documentation:

  • dokkaHtml: This task generates documentation in HTML format.

Experimental formats

  • dokkaGfm: Generates documentation in GitHub Flavored Markdown format.
  • dokkaJavadoc: Generates documentation in Javadoc format.
  • dokkaJekyll: Generates documentation in Jekyll compatible Markdown format.

By default, the generated documentation can be found in the `build/dokka/{format}` directory of your project. Configurations allow for modifications such as changing the location of output.

Multi-project builds

To generate documentation for multi-project builds, apply the Gradle plugin for Dokka within all subprojects needing documentation and their parent project.

MultiModule tasks

MultiModule tasks are designed to generate individual documentation for each subproject using Partial tasks, collect, and process all outputs, and produce complete documentation with a common table of contents as well as resolved cross-project references.

For parent projects, Dokka automatically creates the following tasks:

  • dokkaHtmlMultiModule: This task generates multi-module documentation in HTML output format.

HTML Documentation

Dokka enables HTML documentation generation you can personalize them. Configuration of the HTML format is done via DokkaBase and DokkaBaseConfiguration classes:`

import org.jetbrains.dokka.base.DokkaBaseConfiguration

buildscript {
    dependencies {
        classpath("org.jetbrains.dokka:dokka-base:1.9.10")
    }
}

tasks.withType<DokkaTask>().configureEach {
    pluginConfiguration<DokkaBase, DokkaBaseConfiguration> {
        customAssets = listOf(file("my-image.png"))
        customStyleSheets = listOf(file("my-styles.css"))
        footerMessage = "(c) 2022 MyOrg"
        separateInheritedMembers = false
        templatesDir = file("dokka/templates")
    }
}

The HTML format supports customization, allowing your documentation to have a unique look and feel. You can use your own stylesheets by using the customStyleSheets configuration option or override Dokka's default stylesheets and assets.

Dokka allows the modification of FreeMarker templates used for generating documentation pages. You can override any template by using the templatesDir configuration option, which includes modifying the header, adding your own banners/menus/search, loading analytics, changing body styling, and more. The source code for all of Dokka's templates is available on GitHub.

Please refer to the full documentation for a comprehensive list of the configuration options, templates, directives, and variables available.

For example for this code:

/**
 * Person model class
 * @property name String The name of the person
 * @property age Int The age of the person
 * @property dni String The id card of the person
 * @author Hyperskill
 * @since 1.0
 * @constructor Creates a Person object
 */
data class Person(val name: String, val age: Int, val dni: String) {
    /**
     * Checks if the person is an adult
     * @return true if the person is an adult, false otherwise
     * @since 1.0
     * @see Person
     */
    fun isAdult(): Boolean {
        return age >= 18
    }
    
    /**
     * Checks if the person is a teenager
     * @return true if the person is a teenager, false otherwise
     * @since 1.0
     * @see Person
     */
    fun isValidDni(): Boolean {
        if (!Regex("[0-9]{8}[A-Z]").matches(dni)) {
            return false
        }
        val letter = dni.last()
        val number = dni.substring(0, 8).toInt()
        val remainder = number.toInt() % 23
        val letters = "TRWAGMYFPDXBNJZSQVHLCKE"
        val correctLetter = letters[remainder]
        return letter == correctLetter
    }
}

When you run ./gradlew dokkaHtml, you can generate the doc in html.

Conclusion

Documenting our code is a good practice and a hallmark of quality. For this, KDoc and Dokka aid us by offering powerful tools to enhance code quality through simple annotations. Now, it's time to review what we've learned with some tasks. Let's go!

7 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo