
# Dependencies Management Guideline

How we specify dependency versions affects **stability, security, and performance**.

This document explains how to write dependency version specifiers properly and consistently.

---

## TLDR

You want to add new package `abc` with the latest version `1.2.3`

Specify the version by:

| Specifier | Meaning | Notes                                           |
| --- | --- |-------------------------------------------------|
| `abc~=1.2` | Lock major version | PREFERABLE and default                          |
| `abc~=1.2.3` | Lock minor version | EXPLAIN with a comment                          |
| `abc` | Open specifier | For dev dependencies                            |
| `abc==1.2.3` | Pin to the exact version | AS EXCEPTION, TEMPORARY, EXPLAIN with a comment |

---

## 1. Open version

If you **omit the version**, uv will install the **latest available release**.

```toml
decorator = ""
# or
decorator = "*"
```

**When to use:**
- Internal libraries we fully control.
- Very stable utility libraries with minimal risk.
- Development-only dependencies where breaking changes are acceptable.

**Warning:** Using the latest can lead to unexpected breakages if upstream introduces incompatible changes.

---

## 2. Prefer flexible but controlled ranges

Pin to major by default. But for specific cases it is OK to pin to minor.

The preferred approach is to **allow safe updates** while preventing untested breaking changes.

### ✅ Tilde expressions (`~=`)

Use the `~=` operator (PEP 440 compatible release clause) to allow **patch-level updates**, while staying within a defined minor version.

### Example A: `~=4.4.2`
```toml
"decorator~=4.4.2"
```
- **Allowed versions:** `>=4.4.2, <4.5.0`  
- Meaning:
  - ✅ Patch updates within `4.4.x` are allowed (e.g., `4.4.2 → 4.4.10`)  
  - ❌ Minor updates `4.5.0` and above are **not** allowed

*Use this when you want strict stability — only patch releases allowed.*

### Example B: `~=4.4`
```toml
"decorator~=4.4"
```
- **Allowed versions:** `>=4.4.0, <5.0.0`  
- Meaning:
  - ✅ Patch and minor updates within the major version are allowed (e.g., `4.4.0 → 4.9.9`)  
  - ❌ Major updates `5.0.0` and above are **not** allowed

*Use this when you trust the library to keep backward compatibility across minor versions.*

### Comparison: `~=4.4.2` vs `~=4.4`

| Specifier     | Allowed Range        | Good For |
|---------------|----------------------|----------|
| `~=4.4.2`     | `>=4.4.2, <4.5.0`    | Maximum stability — only patches within `4.4.x` |
| `~=4.4`       | `>=4.4.0, <5.0.0`    | More flexible — allows both minor and patch upgrades within major version |

---


## 3. Exact version pinning (`==`)


❌ **Bad practice** unless absolutely required (see below).


**Example:**
```toml
[project]

dependencies = [
  "requests==2.31.0"
]
```

### Why exact pinning is bad:
- **No automatic updates** – bug fixes and security patches are missed unless we manually bump versions.  
- **Upgrade bottleneck** – upgrading becomes a huge manual effort later.  
- **Conflicts with other libraries** – dependency resolver may fail if another package requires a slightly different version.  
- **Reduced flexibility** – locks our project to a single environment state, making cross-project reuse harder.

**Rule:**  
> Do **not** use `==` unless there is a **critical and well-documented reason**.  
> If you must pin, **add a comment explaining why**.

**Example with justification:**
```toml
  "requests==2.31.0"  # Pinned due to regression in 2.32.x (see DS-XXXX)
```

### Handling transitive dependency issues

When a bug appears due to a **transitive dependency** (dependency-of-a-dependency):

- **Temporarily pin** the problematic library with `==`.
- **Add a clear comment** explaining the reason and reference the issue/ticket.
- **Remove the pin** once the upstream fix is available.

Example:
```toml
urllib3 = "==2.1.0" # Temporary fix for bug introduced in 2.1.1 — see issue #456
```
