# Project and Environment Variables

Project and Environment variables allow you to parameterize logic in your dex project so it can behave differently depending on where or how it’s running. They’re most commonly used to avoid hardcoded values, toggle configurations between environments, and securely inject credentials or tokens using secrets.

These variables follow a familiar pattern used in templated analytics workflows and are evaluated at **compile time**, making them accessible inside models, macros, snapshots, seeds, and more.

{% hint style="warning" %}
Don’t confuse transformation variables with secrets stored in dex's [S](https://docs.dexlabs.io/lakehouse-platform/variables-and-secrets)[ecrets Manager](https://docs.dexlabs.io/lakehouse-platform/variables-and-secrets).\
Variables are used to control logic at compile time (like schema names or flags), while secrets are encrypted values (like passwords or tokens) used to securely configure external connections.
{% endhint %}

### Where Variables Come From

Variables in dex are defined in two ways:

* **Project variables**: Defined in the `dbt_project.yml` file. These are global to the entire dex project and apply across all environments.
* **Environment variables**: Defined in the dex UI, scoped to a specific environment (e.g., `dev`, `staging`, `production`). These override project-level defaults and are evaluated first when resolving variables.

You can also mark any environment variable as a **secret**, ensuring it is encrypted and hidden after being saved.

### How to Define Project Variables

Inside your project, you can define variables in `dbt_project.yml` like this:

```yaml
vars:
  start_date: '2023-01-01'
  schema_suffix: '_dev'
```

These variables are accessible anywhere in your models using the `var()` function.

### Using Variables in Your Models

You can access both project and environment variables using the `var()` Jinja function:

```sql
select *
from raw.customers
where created_at >= '{{ var("start_date") }}'
```

You can also use them for logic in macros, seeds, snapshots, and materialization configs:

```sql
{% if var('schema_suffix') == '_prod' %}
  -- production logic
{% else %}
  -- dev logic
{% endif %}

```

Variables are **evaluated at compile time**, meaning they are rendered before your model or macro runs in the warehouse.

### Precedence

When a variable with the same name is defined in both the project and the environment:

* **Environment variable wins** — it will override the project-level default during execution.

This makes it easy to test safely in dev while preserving production configurations in place.

### Example Use Case

Let’s say you want to limit row output in development but run full datasets in production:

* Project-level `dbt_project.yml`:

```yaml
vars:
  row_limit: null
```

* Environment override (dev):

```makefile
Name: ROW_LIMIT  
Value: 100  
Scope: Environment  
Environment: dev
```

* Model logic:

```sql
select *
from {{ ref('orders') }}
{% if var('row_limit') %}
limit {{ var('row_limit') }}
{% endif %}
```

In dev, this model limits rows to 100. In production, it runs without a limit.

### Best Practices

* Use `var('...')` to make behavior configurable across projects or stages
* Always give defaults in `dbt_project.yml` so local runs don’t break
* Use uppercase names with underscores for consistency (`SCHEMA_NAME`, `LIMIT_ROWS`)
* Use secrets for anything sensitive (API tokens, passwords)
* Document variable usage in your project README or `.yml` files
