A variable can help you store a piece of information once and reuse it (a name, a count, a reference to a connection) anywhere Buttons lets you reference variables. Which variables you can see and use at a given point depends on scope: where a variable was defined determines where it's visible. Understanding scope up front avoids the most common variable mistake: two variables with the same name that turn out to be in different scopes, and don't refer to the same value at all.
Connection: a reference to one of your configured connections, used to swap which connection a control targets at runtime rather than fixing it permanently.
Routing Endpoint: a reference to a routing port or bundle, the routing equivalent of a Connection variable.
Feedback: a read-only value driven directly by a connection's own feedback, rather than something you set yourself. Defining one means picking both the feedback and, if that feedback takes its own options (such as which input to watch), those options too: set once when you create the variable, not per position.
Self (inside one element only)→ Parent (the element's own parent, exposed to it)→ Position (global definition, one value per position)→ Workflow / Connection (global, one shared value everywhere)
A Self variable lives inside the specific button, section, or similar element where you define it. It isn't visible anywhere else, not even to a sibling copy of the exact same button. Two separate buttons can each have a Self variable with the same name and different values without conflicting, because neither can see the other's.
A Parent variable is defined inside an element, but it's exposed as a Self variable of that element's parent instead of the element itself. This is how a button placed inside a section can expose a value the section itself can then read as its own Self variable: the naming describes the relationship, not a separate mechanism.
Reference a Parent variable as $(parent.state.name).
A Position Variable's definition is global (creating one makes it available in every position), but its value is set independently per position. This means the variable's name and type are shared everywhere, while what it actually holds depends on which position you're looking at.
A Position Variable can hold a Connection-type value, which is what lets a single position swap which connection its buttons actually control: set the variable to a different connection, and every action using it follows, without editing each button individually.
Reference a Position Variable as $(position.state.name).
Change a Position Variable's value with the Set Position Variable Internal Action.
A Workflow Variable is global for both reading and writing, from anywhere in Buttons, not scoped to the workflow that created it. Creating one requires a Workflow, since that's where the variable is defined and typed, but once created it's just as visible everywhere as a Connection Variable is.
Change a Workflow Variable's value with the Set Workflow Variable Internal Action.
A Connection Variable is supplied by a connection itself: Buttons doesn't let one connection see or change another's variables directly, but a connection's own actions and feedbacks can manipulate its variables, and other parts of Buttons can read them. Each connection has its own Variable Key, set on its Config tab; the full reference combines that key with the variable's own name, such as $(MyAtem.device_ip).
If you run more than one connection using the same module, give each a distinct Variable Key, otherwise you can't tell their variables apart by name.
No two variables visible in the same place can share a name, but variables in different scopes can, safely, precisely because they're never visible together. Use Variable Scope itself as your main tool for avoiding collisions: keep a value in Self or Parent scope unless it genuinely needs to be seen from elsewhere, and reach for Position or Workflow scope only once something actually needs to be global.