This isn't a criticism of Mix's current design - I like that `mix.exs` is transparent and editable. But for the common case of adding or removing a dependency, there's unnecessary friction compared to other modern package managers like cargo in Rust or uv in Python.
Adding a dependency currently means: open `mix.exs` → find the `deps` function → remember the tuple syntax → look up the version format → add the entry → save → run `mix deps.get`.
For complex cases with conditional logic or special constraints, editing the file directly makes sense. But for "add this package from Hex" it's more ceremony than needed.
ProposalAdd two new Mix tasks: `mix deps.add` and `mix deps.remove` that handle the common case of adding or removing dependencies from your project.
### `mix deps.add`
Adds one or more dependencies to your `mix.exs` file and fetches them.
```bash
# Add latest version from Hex
mix deps.add phoenix
# Add with version requirement
mix deps.add phoenix ~> 1.7
# Add from git
mix deps.add phoenix --git
https://github.com/phoenixframework/phoenix.git# Add from GitHub (shorthand)
mix deps.add phoenix --github phoenixframework/phoenix --tag v1.7.0
# Add from local path
mix deps.add my_lib --path ../my_lib
# Add with options
mix deps.add phoenix --only dev
mix deps.add benchee --only test --optional
mix deps.add jason --override
# Add to umbrella app
mix deps.add my_app --in-umbrella
# Mix and match multiple packages
mix deps.add phoenix ecto_sql postgrex
```
### `mix deps.remove`
Removes one or more dependencies from your `mix.exs` file.
```bash
# Remove a package
mix deps.remove phoenix
# Remove multiple packages
mix deps.remove phoenix ecto postgrex
```
## Options
The commands would need to support Mix's existing dependency options:
- Source: `--git`, `--github`, `--path`, `--in-umbrella`
- Git: `--tag`, `--branch`, `--ref`, `--sparse`, `--subdir`, `--submodules`
- Environment: `--only`, `--targets`, `--optional`, `--override`, `--runtime`, `--env`
- Hex: `--hex`, `--repo`
- Behavior: `--no-fetch` (skip `mix deps.get` after modifying `mix.exs`)
## More Examples
```bash
# Test-only dependency
mix deps.add ex_machina --only test
# Optional dependency (for library authors)
mix deps.add hackney --optional
# Override conflicting transitive dependency
mix deps.add tesla --override
# Local development
mix deps.add my_lib --path ~/projects/my_lib
# Umbrella app
cd apps/my_workers && mix deps.add my_core --in-umbrella
# Git with specific options
mix deps.add phoenix_live_view --github phoenixframework/phoenix_live_view --tag v1.0.0
```
The tricky part is modifying `mix.exs` while preserving formatting, comments, and custom logic. AST manipulation can handle the parsing, but needs care with formatting.
For version resolution, when no version is specified, query Hex for the latest and use `~> MAJOR.MINOR`.
The generated deps should look like hand-written ones:
```elixir
{:phoenix, "~> 1.7"}
{:credo, "~> 1.6", only: [:dev, :test], runtime: false}
{:my_lib, path: "../my_lib"}
```
These commands are for common cases. Complex scenarios with conditional logic still need direct `mix.exs` editing - that's fine. This is about making "add this Hex package" frictionless, not about replacing the file-based approach entirely.
I'm happy to discuss further or implement this if the proposal seems reasonable.