pub, pubspec and the analyzer
pubspec.yaml is the passport of a package — it describes both its dependencies and the package itself.
The main fields:
name— the package name inlower_snake_case;import 'package:<name>/...'is derived from it.version— a semantic version (major.minor.patch); it matters for published packages.environment: sdk:— which Dart SDK versions it works with. The most frequently forgotten field: withoutsdk: ^3.6.0, code using records or pattern matching fails on an older SDK with a confusing error.dependencies— libraries the package needs to run. Everyone using your package downloads these too.dev_dependencies— only for development and testing:test,lints,build_runner,mocktail. They are not propagated to consumers — which is why puttingtestintodependenciesby mistake is a classic error.dependency_overrides— a temporary way past a version conflict. Left in place it is technical debt, and it must never appear in a published package.
name: billing_core
description: Domain logic for invoices and payments.
version: 1.4.2
environment:
sdk: ^3.6.0 # which SDKs this package supports
dependencies:
http: ^1.2.0 # needed at runtime by every consumer
collection: ^1.19.0
meta: ^1.16.0
dev_dependencies:
test: ^1.25.0 # only for developing THIS package
lints: ^5.0.0
coverage: ^1.11.0
# Temporary escape hatch only - never in a published package.
# dependency_overrides:
# http: 1.2.1A typical pubspec.yaml — dependencies versus dev_dependencies
| Constraint | What it allows | Note |
|---|---|---|
| `^1.4.2` | `>=1.4.2 <2.0.0` | The default choice: new features and fixes arrive, breaking changes do not |
| `^0.3.1` | `>=0.3.1 <0.4.0` | Before 1.0.0 the **minor** number counts as breaking — a favourite interview subtlety |
| `>=1.2.0 <1.5.0` | A manually written range | When there is a concrete reason (a known regression); document it in a comment |
| `1.4.2` | That exact version only | Effectively forbidden in a library: it forces version conflicts on consumers |
| `any` | Any version | Dangerous in practice — a future major release can break your package |
| `path: ../billing_core` | From a local folder | For monorepos and local development; cannot be published |
| `git: {url: ..., ref: v1.4.2}` | From a repository at a specific ref | Fine for internal packages; pin `ref` to a tag or commit, never a branch |
`pubspec.lock` records the exact resolved versions.
- Applications (apps, CLIs, Flutter apps) — commit the lock file so the team and CI build the same versions and "works on my machine" disappears.
- Libraries (published packages) — do not commit it: consumers resolve their own versions, your lock file has no effect on them and only adds noise.
Everyday commands:
dart pub get— installs what the lock file pins (this is what CI runs).dart pub upgrade— moves to the newest versions the constraints allow and rewrites the lock file.dart pub upgrade --major-versions— also raises the constraints themselves (may be breaking).dart pub outdated— changes nothing and reports what is behind and whether a constraint must move.dart pub deps— the dependency tree; invaluable when chasing a conflict.
Static analysis is one of the strongest parts of the Dart ecosystem:
dart analyze— type errors plus lint rules; in CI run it with--fatal-infosso informational messages block too.dart format— one non-negotiable style; in CI,dart format --output=none --set-exit-if-changed .analysis_options.yaml—include: package:lints/recommended.yaml(for packages) orpackage:flutter_lints/flutter.yaml, plus your team's rules underlinter: rules:.dart fix --dry-run/dart fix --apply— automatically fixes a large share of lint warnings; it is the first command to run after an SDK upgrade.
Interview tip: for "what does your CI run for a Dart package?", list the commands in order and with the reason — it sounds prepared: dart pub get → dart format --output=none --set-exit-if-changed . (takes formatting debates out of code review) → dart analyze --fatal-infos (stops warnings from accumulating) → dart test --coverage=coverage → for a published package, dart pub publish --dry-run and dart pub outdated. Then add one sentence: "dart fix --apply runs locally, not in CI — CI should report the mismatch, not rewrite the code."
📚 Sources and documentation
- The pubspec fileofficialdart.dev
- Static analysisofficialdart.dev