Lesson 1

The compiler/runtime/node pipeline

Draw the five boundaries between a composable call and a target UI node.

3 min readUpdated Jul 11, 2026

Status: The five boundaries are Durable . Method names and compiler output are Version-specific at the pinned revisions above. This lesson describes a source snapshot, not every released Compose artifact.

Release mapping: No released artifact is asserted here. Check the Compose Runtime release notes before mapping these private details to a Maven version.

Outcome

You should be able to point to the owner of each step:

Kotlin source → compiler protocol → composition memory → applied changes → UI nodes

That map is more useful than saying that Compose “builds a view.”

A composable does not return a view

Consider this function:

@Composable
fun Greeting(name: String) {
    Text("Hello, $name")
}

Greeting returns Unit. It does not return a Text object to its caller. The @Composable annotation changes the function type and gives the call an implicit composition context. That context lets the runtime relate this execution to earlier executions at the same logical position (Composable.kt, KDoc).

The first question is therefore not “what widget came back?” Ask instead:

What protocol did the compiler and runtime use while this body executed?

The five boundaries

Diagram
flowchart TD
    Source[Kotlin source] --> Protocol[Compiler protocol]
    Protocol --> Memory[Composition memory]
    Memory --> Changes[Calculated changes]
    Changes --> Applier[Applier operations]
    Applier --> Nodes[Target UI nodes]

1. Source

The Kotlin body describes what belongs at this position. It is declarative input. It is not the target node tree.

2. Compiler protocol

The Compose compiler lowers the body into calls on Composer. The generated code can carry groups, keys, changed flags, and node operations. Exact keys and masks are Version-specific compiler output. The pinned ComposableFunctionBodyTransformer.kt is the producer to inspect.

3. Composition memory

The runtime records groups, slots, remembered values, reads, and invalidation scopes. This is the runtime’s memory of a composition. It is not a second name for the rendered tree.

4. Applied changes

Composition calculates operations before it applies them. A pending change can insert, remove, move, or update something. The calculation phase and the application phase are separate Durable boundaries.

5. Target nodes

An Applier owns a target-specific tree. In Compose UI, the concrete bridge leads to ComposeUiNode and LayoutNode. The node tree is changed through applier operations, not by every composable invocation.

Trace Greeting("Ada")

On first composition, a useful trace is:

  1. The compiler-generated body enters its protocol groups and calls Text with a composer context. Version-specific
  2. The runtime records composition information and node-producing operations. A wrapper can run without producing a node. Durable boundary.
  3. Composition calculates changes. The target tree has not received those operations yet. Durable
  4. applyChanges sends operations to the applier. The Compose UI applier creates or updates LayoutNode objects. Version-specific concrete bridge.
  5. The apply batch ends. Recorded lifecycle callbacks and side effects run according to the runtime’s apply ordering. Version-specific

A later recomposition may update an existing node instead of inserting another one. “Recomposition” does not mean “rebuild every node.”

Keep these ideas separate

  • Composable function: Kotlin code participating in a compiler/runtime protocol.
  • Composition memory: groups and slots used to remember structure and state.
  • Change list: calculated work waiting for application.
  • Applier: the owner of target-tree mutations.
  • LayoutNode: Compose UI’s concrete node type in this bridge.

Check yourself

If a composable reads state but never calls ComposeNode, which boundary can still change, and which boundary need not? Explain your answer using the five-part map.

Source notes

ClaimDirect evidenceStatus
@Composable changes the function type and carries composition contextComposable.ktDurable
Compiler code produces calls consumed by ComposerComposer.kt and pinned ComposableFunctionBodyTransformer.ktVersion-specific
Composition calculates before applicationComposition.kt, ControlledCompositionDurable Version-specific
An applier owns target-tree operationsApplier.ktDurable
Compose UI bridges to LayoutNodeComposeUiNode.kt and LayoutNode.ktVersion-specific

Freshness

Recheck this lesson when compiler lowering, ControlledComposition, the applier contract, or the Compose UI node bridge changes. Refresh the two pinned revisions and the release notes before changing the model.

Finished this lesson?

Your progress stays only in this browser.