Kotlin's Type-Safe Builders: Code as Data

Type-safe builders use Kotlin code to create a custom language (DSL) for building complex objects. It's like writing HTML, but the compiler validates your structure. This is common for UI layouts or server configs. The footgun is omitting @DslMarker.
Why it exists
Manually building complex, nested objects—like an HTML document tree—is verbose and error-prone. Procedural code like parent.addChild(new Child()) obscures the final structure. Type-safe builders provide a declarative, readable syntax that mirrors the object hierarchy itself, while retaining the safety of the Kotlin type system.
The mental model
Think of it as writing HTML or XML directly in your Kotlin code, but with the compiler as a strict validator. Instead of writing , you write p { ... }. The curly braces define a scope where only valid child elements can be added. You're not writing a string; you are assembling a tree of actual Kotlin objects.
How it works
The core mechanism is "function literals with receiver." A top-level function like html() accepts a lambda with a special type, HTML.() -> Unit. This means that inside the lambda's {...} block, the keyword this refers to a newly created HTML object. You can then call methods on it, like head(), without explicitly writing this.. The head() function, in turn, takes a Head.() -> Unit lambda, creating a nested scope for the <head> tag. This pattern repeats, building the entire hierarchy.
When to use it
Use builders when you need to construct complex, hierarchical data structures in a semi-declarative way. Three common use cases: first, generating markup like HTML or XML; second, configuring application components, such as defining server routes in frameworks like Ktor; third, creating structured test data in a readable format.
When not to use it
Avoid builders for simple object creation where a standard constructor or factory function is clearer and has less overhead. They are overkill if the object structure isn't hierarchical or deeply nested. The complexity of defining the builder classes only pays off for complex structures.
One canonical example
An HTML builder is the classic example. The code html { head { title { +"My Site" } } } doesn't generate a string. It calls a series of functions that instantiate and link HTML, Head, and Title objects into a tree. The crucial @DslMarker annotation prevents invalid nesting, for example, by stopping you from calling head {} from within a body {} block. The final result is a root HTML object that can then be rendered into a string.
Interview question
What is the immediate output of a successfully executed Kotlin type-safe builder block?
- a.A hierarchical tree of interconnected Kotlin objects representing the defined structure.Correct
- b.A set of validation rules ensuring that the generated structure adheres to specific type constraints.
- c.A string formatted according to the builder's structure, such as HTML or XML.
- d.A compiled DSL definition that can be reused to generate various data structures.
Why? this is the answer
The card explicitly states that a builder block "doesn't generate a string" but rather "instantiate and link HTML, Head, and Title objects into a tree," with the "final result is a root HTML object." While builders are often used to eventually produce strings, their immediate output is an object hierarchy. Option C is a common misconception because builders are frequently used for markup generation.
Just read this? Test yourself on what you have been reading.
Read the original → kotlinlang.org
You just looked this up. Could you explain it out loud?
That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on kotlin — each one lists the topics its interview covers.
See open roles