Python Atlas
Software engineering

Dependency decisions

Apply a rigorous framework when choosing between the Python standard library and external packages.

Dependencies exchange implementation effort for capability and maintenance obligations. “Use the standard library” and “do not reinvent the wheel” are both incomplete rules. Make the choice from requirements, evidence, and total ownership cost.

Start with the requirement

Write the required capability before naming a package:

  • supported Python and operating-system versions;
  • input scale, latency, throughput, and memory limits;
  • correctness, security, and compliance constraints;
  • sync or async execution model;
  • public API stability and serialization formats;
  • deployment limits such as offline installation or binary wheels;
  • expected maintenance lifetime.

Then build the smallest representative spike. Do not evaluate libraries only from tutorials.

Evaluate the standard library

Prefer the standard library when it fully satisfies the contract with clear code. Benefits include coordinated Python releases, fewer supply-chain inputs, broad availability, and strong documentation.

Examples include:

  • pathlib for filesystem paths;
  • tomllib for reading TOML;
  • json, csv, and sqlite3 for common data formats and storage;
  • argparse for dependency-free CLIs;
  • logging for baseline application logs;
  • urllib.request for limited HTTP needs;
  • unittest and doctest for built-in testing requirements.

Stdlib does not automatically mean simpler. Complex HTTP clients, schema validation, timezone rules, cryptography, and advanced CLI experiences may be safer with focused libraries.

Score external candidates

Use a weighted decision record rather than intuition. Score each criterion from 1 to 5 and multiply by its weight:

CriterionExample weightEvidence
Requirement coverage5Spike against real inputs
Correctness and security5Advisories, design, release history
Maintenance health4Recent releases, issue handling, bus factor
API stability4SemVer practice, migration guides
Operational fit4Startup, memory, wheels, platform support
Dependency footprint3Direct and transitive dependency tree
Team familiarity2Ability to debug and maintain
Exit cost3Isolation and migration effort

Weights must reflect the project. A command-line utility distributed as a single binary may weight footprint highly; an internal service may prioritize observability integration.

Popularity is not proof

Downloads and stars can help discover candidates, but they do not establish correctness, security, maintenance quality, or fit for your workload.

Inspect the whole dependency

Before adoption, verify:

  • license compatibility;
  • release cadence and unresolved security advisories;
  • supported Python versions and platforms;
  • availability of source distributions and required wheels;
  • transitive dependencies and optional extras;
  • maintainer responsiveness and governance;
  • documentation for failure modes, timeouts, and migration;
  • behavior under malformed and adversarial inputs.

Install the package into an isolated environment and inspect the resolved dependency graph. Import and startup cost matter for short-lived tools and serverless workloads.

Isolate volatile integrations

Wrap an external package only when the wrapper expresses your application's contract:

from dataclasses import dataclass
from typing import Protocol


@dataclass(frozen=True)
class Weather:
    temperature_celsius: float
    condition: str


class WeatherClient(Protocol):
    def current(self, city: str) -> Weather: ...

An adapter can use a particular HTTP client and provider. The application depends on WeatherClient, not vendor response objects. Avoid wrappers that merely rename every method; they add maintenance without reducing coupling.

Pinning and updates

Reusable libraries should normally declare compatible dependency ranges and test their lower and upper supported bounds. Deployable applications should produce a reproducible environment with a lock file or hashes appropriate to their toolchain.

For every dependency:

  1. assign an owner;
  2. enable vulnerability and update monitoring;
  3. review changelogs before major upgrades;
  4. run tests against the resolved environment;
  5. schedule removal of unused packages;
  6. record exceptions for abandoned but unavoidable software.

An exact pin without an update process freezes known vulnerabilities as effectively as it freezes working code.

Build versus buy decision sequence

  1. Can the standard library meet all must-have requirements clearly?
  2. Is the capability security-sensitive or deceptively complex?
  3. Is an external candidate demonstrably better on weighted criteria?
  4. Can the project operate, update, and debug that dependency?
  5. Can integration be isolated behind a small application-facing contract?
  6. What is the migration plan if maintenance stops?

For cryptography, authentication protocols, parsers for hostile formats, and timezone data, homegrown implementations carry unusually high correctness and security risk.

Pitfalls

  • adding a package for one trivial helper;
  • comparing source line count instead of total ownership cost;
  • relying on an unmaintained package because its API is familiar;
  • importing vendor models throughout domain code;
  • using loose application dependencies without reproducible resolution;
  • pinning forever without monitoring updates;
  • building security-sensitive algorithms in-house;
  • failing to test optional features and platform-specific wheels.

Decision checklist

  • Requirements and constraints are written before candidate selection.
  • A stdlib solution has been evaluated honestly.
  • External candidates are tested with representative inputs.
  • Security, license, maintenance, and transitive dependencies are reviewed.
  • The dependency has an owner and update policy.
  • Integration is isolated where replacement risk is meaningful.
  • Reproducible installation is verified on supported platforms.
  • The decision and reversal trigger are recorded.

Practice

Evaluate an HTTP client for a CLI that performs three requests per run. Compare urllib.request with one external candidate. Define weighted criteria, implement both spikes, test timeout and invalid-certificate behavior, inspect dependency trees, and write a short decision record with a reconsideration trigger.

LEARNING RECORD

Finish this lesson

Mark it complete when you can explain the main decision without looking.

KNOWLEDGE CHECK

Dependency decisions

1What should happen before selecting an external package?
2When does wrapping an external dependency meaningfully reduce coupling?