principles_of_developing_robust_applications

# Principles of developing robust applications

Kotlin edition

[TOC levels=2-6]

## Making illegal state unrepresentable

### Union types

### Validate at the boundary

### Null handling

### Enumerating all cases

### Specific types with value classes

## Exceptions and how to handle them

## Logging

Log as much as necessary but as little as possible. Messages should have enough context to be valuable. This means if you read a log message it should be useful. Who did what, when and where: "Document updated" is less useful than: "User A updated document D, changing the title from Y to Z."

### Log Levels

* Verbose: Use this to print raw input / output streams.
* Debug: Provide context that is useful for debugging, i.e. making internal state visible.
* Info: Use this to log actions that were done purposefully
* Warning: Use this for errors that can be recovered from.
* Error: Use this for unrecoverable errors.

### Log rotation

Don't forget to enable log rotation, otherwise your application will go down unexpectedly because of a full disk.

## Memory usage

If possible, try to keep the memory usage constant when working with external resources. Use streams instead of loading them into memory if the size is undefined.

For example, this takes as much memory as the file is big. If the file's bigger than the JVMs heap allowance, it dies with an OOM.

```kotlin

    // read file content into memory
    val content = Files.readAllBytes(Paths.get("source.jpg"))

    // write file content to disk
    Files.write(Paths.get("target.jpg"), content)
```

On the other hand, the following code:
```kotlin
    FileInputStream("source.jpg").use { inputStream ->
        FileOutputStream("target.jpg").use { outputStream ->
            inputStream.copyTo(outputStream, 4096)
        }
    }
```
takes roughly 4096 bytes at once, no matter how big the file is.


edited by: stefs at Friday, October 17, 2025, 1:06:57 PM Coordinated Universal Time


view