← Back

Trees, Nodes, and Why Structure Matters for Agent Coding

Code isn't just text. It's a tree.

When you write const x = 5, you're not just putting characters on a screen. You're creating a variable declaration node with an identifier child and a numeric literal child. The compiler sees this tree immediately. We humans? We've trained ourselves to see it too, but it takes years.

The Tree is the Truth

Every programming language parser converts your code into an Abstract Syntax Tree (AST). This isn't an implementation detail—it's the fundamental nature of code. The tree structure encodes meaning: parent-child relationships show scope, sibling order shows execution sequence, node types define semantics.

function greet(name) {
  return `Hello, ${name}`;
}

This isn't seven lines of text. It's a FunctionDeclaration node containing:

  • An Identifier node ("greet")
  • A Parameters list with one Identifier ("name")
  • A BlockStatement containing a ReturnStatement
  • Which contains a TemplateLiteral with expressions

The tree is what matters. The text is just serialization.

Here's what the AST actually looks like:

FunctionDeclaration
id: Identifier
name: "greet"
params: [
Identifier
name: "name"
]
body: BlockStatement
body: [
ReturnStatement
argument: TemplateLiteral
quasis: ["Hello, ", ""]
expressions: [
Identifier
name: "name"
]
]

See the structure? That's what the compiler works with. The indentation, the newlines, the spacing—all gone. Just nodes and relationships.

Why This Matters for Agents

When AI agents write code, they're doing something fundamentally different from humans. We type linearly, left to right, top to bottom. We think in text because that's how we learned. Agents don't have that constraint.

An agent could theoretically work directly with AST nodes—building trees, not strings. But they don't, because they're trained on text. They've learned our serialization format, not the underlying structure. This creates a fascinating tension: agents are powerful enough to understand code structure deeply, but constrained by the text-based interface we've given them.

Consider how an agent sees this code:

class DataStore {
  private cache: Map<string, any>;
  
  constructor() {
    this.cache = new Map();
  }
  
  get(key: string): any {
    return this.cache.get(key);
  }
}

The agent doesn't just see characters. It recognizes:

ClassDeclaration "DataStore"
PropertyDefinition "cache"
accessibility: private
typeAnnotation: Map<string, any>
MethodDefinition "constructor"
body: assigns this.cache
MethodDefinition "get"
params: [key: string]
returnType: any
body: returns cache.get(key)

The agent understands the relationships: get method accesses cache property, constructor initializes it, everything is scoped to the class. This is structural understanding, not pattern matching.

The Philosophy of Structure

Here's what matters: good code structure makes the tree obvious from the text.

When you write deeply nested callbacks, you're creating a tree that's hard to visualize from the serialized form. When you extract functions with clear names, you're making the tree's semantic structure match its syntactic structure. When you keep files small and focused, you're creating subtrees that fit in working memory.

This isn't about "clean code" aesthetics. It's about making the tree structure legible.

Compare these two approaches:

Hard to parse:

getData().then(r => processData(r).then(p => saveData(p).then(s => log(s))))

Tree structure visible:

const raw = await getData();
const processed = await processData(raw);
const saved = await saveData(processed);
log(saved);

The second version makes the tree obvious:

Block
VariableDeclaration "raw"
init: await getData()
VariableDeclaration "processed"
init: await processData(raw)
VariableDeclaration "saved"
init: await saveData(processed)
ExpressionStatement
CallExpression: log(saved)

Four sequential operations. No nesting. The tree is flat and clear.

What Agents See

Agents are surprisingly good at navigating code trees. They can:

  • Jump between related nodes (find all references)
  • Understand scope chains (what variables are available where)
  • Recognize patterns across subtrees (this looks like that)
  • Refactor while preserving tree semantics

But they struggle when the tree structure is obscured by:

  • Implicit dependencies (global state, side effects)
  • Dynamic behavior (eval, reflection, metaprogramming)
  • Context that exists outside the tree (environment variables, external systems)

Here's what makes agent-friendly code:

// ✓ Clear tree structure
function calculateTotal(items: Item[]): number {
  const subtotal = items.reduce((sum, item) => sum + item.price, 0);
  const tax = subtotal * TAX_RATE;
  return subtotal + tax;
}

// ✗ Hidden dependencies
function calculateTotal(items: Item[]): number {
  // Where does globalCart come from? 
  // What does applyDiscounts modify?
  applyDiscounts(globalCart);
  return globalCart.total;
}

The first function is a pure subtree. Input → computation → output. The agent can understand it in isolation.

The second function has invisible edges in the graph. globalCart exists somewhere else. applyDiscounts might modify state. The tree structure lies about the actual dependencies.

Writing for Trees

If you're writing code that agents will work with—and increasingly, all code is code that agents will work with—think about the tree:

Make relationships explicit. Import what you use. Pass dependencies as parameters. Let the tree show the connections.

// ✓ Dependencies in the tree
import { logger } from './logger';
import { db } from './database';

function saveUser(user: User, logger: Logger, db: Database) {
  logger.info('Saving user');
  return db.users.insert(user);
}

// ✗ Hidden dependencies
function saveUser(user: User) {
  // Where do these come from?
  logger.info('Saving user');
  return db.users.insert(user);
}

Keep subtrees independent. Pure functions are easy for agents because the tree is self-contained. Side effects create invisible edges in the graph.

// ✓ Self-contained subtree
function formatUser(user: User): string {
  return `${user.name} (${user.email})`;
}

// ✗ Side effects outside the tree
function formatUser(user: User): string {
  analytics.track('user_formatted'); // Side effect!
  return `${user.name} (${user.email})`;
}

Name nodes meaningfully. Variable and function names are metadata on tree nodes. They're how agents (and humans) understand what a subtree does without traversing it.

// ✓ Names describe the node's purpose
const activeUsers = users.filter(u => u.lastSeen > yesterday);
const userCount = activeUsers.length;

// ✗ Names hide the structure
const x = users.filter(u => u.lastSeen > yesterday);
const n = x.length;

Flatten when possible. Deep nesting creates deep trees. Sometimes that's necessary (nested scopes, recursive structures), but often it's accidental complexity.

Shallow tree (good): Deep tree (harder):
Block Block
Statement IfStatement
Statement body: Block
Statement IfStatement
Statement body: Block
IfStatement
body: Block

The Future is Structural

We're moving toward a world where code is increasingly generated, modified, and understood by agents. The better we make our tree structures—and the better we make those structures visible in our text—the more effectively agents can work with our code.

This isn't about writing for machines instead of humans. Trees are how humans understand code too; we've just forgotten that because we learned to read the serialized form so fluently.

The tree is the code. The text is just how we write it down.


This matters because agents are becoming collaborators, not just tools. And collaborators need to understand the structure of what they're working with.