Skip to content

Guidelines

Guidelines for Using Exact vs Range of Versions in Pipfiles

Whether to use an exact version or a range of versions depends on the type of package and the stability you want in your project. Here’s a guideline to help you decide:

1. Core Frameworks and Libraries (e.g., Django, Flask, React)

  • Use a Range of Versions: For widely-used frameworks like Django, it's best to use a range of versions, as these libraries are well-maintained, and backward compatibility is usually handled well. The range allows you to benefit from minor bug fixes and security patches.

  • Example:

    django = ">=3.2, <4.0"  # Pin to Django 3.x versions
    djangorestframework = ">=3.12, <4.0"    # Pin to DRF 3.x versions
    
  • Why Range?: These core libraries release frequent updates with important fixes or new features, and you generally want to keep your project up-to-date within a stable version range (e.g., all 3.x versions).

2. Utility Libraries (e.g., Requests, NumPy)

  • Use a Range of Versions: For well-maintained utility libraries, it’s also a good idea to use a range. These packages typically have a high level of stability, and minor updates rarely break functionality.

  • Example:

    requests = ">=2.25, <3.0"
    numpy = ">=1.19, <1.24"
    
  • Why Range?: These libraries usually evolve incrementally, and staying within a range helps avoid potential breaking changes in major releases.

3. Security or Essential Dependency Packages (e.g., JWT, Auth Libraries)

  • Use Exact Versions: For packages related to security or critical components (e.g., authentication libraries), it is often safer to lock them to an exact version to avoid unexpected changes in security behavior.

  • Example

    pyjwt = "2.1.0"
    cryptography = "3.4.7"
    
  • Why Exact Version?: Security packages can sometimes introduce significant changes even in minor releases, so locking them to a tested version helps ensure stability.

4. Development or Build Tools (e.g., Linters, Testing Libraries)

  • Use Exact Versions or Very Narrow Ranges: For development tools (e.g., flake8, pytest), you may want to use exact versions or narrow ranges to ensure consistent behavior across all environments, especially in CI/CD pipelines.

  • Example:

    flake8 = "3.9.2"
    black = "21.5b0"
    pytest = ">=6.2.4, <6.3"
    
  • Why Exact or Narrow?: These tools directly impact your development workflow, and it’s essential to avoid unexpected changes in formatting, testing, or code linting due to updates.

5. Packages in Active Development or With Known Instability

  • Use Exact Versions: If a package is relatively new, not well-documented, or has a history of breaking changes, it’s best to use an exact version that you’ve tested.

  • Example:

    some-unstable-library = "0.5.2"
    
  • Why Exact Version?: For packages that might introduce breaking changes frequently or are still in early development (pre-1.0 versions), you should lock the version to avoid unexpected behavior.


General Rule of Thumb:

  • Range for stability, minor updates, and flexibility: For mature and well-maintained packages.
  • Exact version for security, critical dependencies, or packages with frequent breaking changes: For security or more unstable libraries.

This balanced approach ensures you get the best of both worlds: flexibility in your main dependencies while avoiding unexpected changes in critical areas.


Here’s an example of an updated Pipfile with these recommendations:

[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
django = ">=3.2, <4.0"
djangorestframework = ">=3.12, <4.0"

[dev-packages]
flake8 = "*"
black = "*"
pytest = "*"
pytest-django = "*"
safety = "*"
bandit = "*"

[requires]
python_version = "3.8"
python_full_version = "3.8.1"

This should make your project more stable and maintainable while following good development practices.


Manual vs Command-Based Editing of Pipfile for Installing Packages

You can either manually edit (hardcode) the Pipfile or use Pipenv commands to automatically add the package with version constraints. Both approaches are valid, but it depends on your preference.

Option 1: Hardcode the Pipfile

You can manually add the version constraint directly into your Pipfile. This is useful if you want full control over the versions and dependencies.

  • Example:

    [packages]
    django = ">=3.2, <4.0"
    

After editing the Pipfile, simply run:

pipenv install

Option 2: Use Pipenv Command to Add Version Constraints

You can also let Pipenv modify the Pipfile for you by using the following command:

pipenv install "django>=3.2,<4.0"

This command will:

  • Automatically add the specified version constraint to your Pipfile under the [packages] section.
  • Install the correct version of Django within the specified range.
Which Method is Better?
  • Hardcoding (Manual Edit): Gives you full control if you want to carefully manage or review your Pipfile. You might prefer this for large projects where you want to keep track of changes in a more controlled manner.

  • Pipenv Command: Faster and easier, especially when adding multiple packages or if you prefer automation. This also ensures that the Pipfile is updated correctly and avoids manual errors.

Both methods achieve the same result, so it's up to your personal workflow preference.


Choosing Between Version Range vs Exact Version for Installing Django with Pipenv

Both commands serve slightly different purposes, and which one is better depends on your project’s needs. Let me explain the differences between the two approaches:

1. pipenv install "django>=4.2,<5.0"

  • What it does: This command installs Django 4.2 or any newer release up to, but not including, version 5.0.
  • Benefits:
    • It gives you flexibility to upgrade to minor releases (like 4.2.1, 4.3, etc.) within the 4.x series automatically when you run pipenv update.
    • Useful if you want to always have the latest bug fixes, security patches, and features in the 4.x series without manually updating the version.
  • Drawback: You might unintentionally install a new minor version that could introduce changes that you’re not prepared for (although minor versions generally avoid breaking changes).

2. pipenv install "django==4.2"

  • What it does: This command locks your project to exactly Django 4.2 (specifically the version 4.2.0).
  • Benefits:
    • Ensures your project stays on Django 4.2.0, without automatically upgrading to newer minor versions like 4.2.1 or 4.3.
    • Provides stability and control if you want to prevent any automatic upgrades and only update manually when you're ready.
  • Drawback: You miss out on automatic updates for bug fixes and security patches within the 4.x series unless you manually upgrade.
Which is Better?
  • If you want flexibility to stay up-to-date with bug fixes, minor feature improvements, and security patches, use:

    pipenv install "django>=4.2,<5.0"
    
  • If you prefer stability and want to control exactly which version of Django is installed without any automatic updates, use:

    pipenv install "django==4.2"
    

DRF 3.14.x is fully compatible with Django 4.2 and Python 3.8.

pipenv install "djangorestframework==3.14.0"

compatible with Django 4.2 and Python 3.8.

pipenv install "django>=4.2,<5.0"
pipenv install "djangorestframework>=3.14,<3.15"

Recommendation:

  • For most projects, especially long-term or production projects, pinning to a specific version (like django==4.2) is often the best choice to avoid unexpected changes.
  • If you are comfortable with handling minor updates and want to stay up-to-date within the 4.x series, then the range version ("django>=4.2,<5.0") works well.