Use caution here, though.

I've completed a half dozen Django upgrades at this point, and every time I ran into abandoned dependencies. The most difficult of those to deal with were Django extensions - and those were by far the most common I ran into.

In my experience, there are three ways those go:

First, the extension's functionality is no longer necessary (or helpful) because it was rolled into Django, a larger/more popular extension, Django changed enough that there's no longer a need for the extension. This is usually fairly easy to deal with. A few (~1/3?) were even updated to include migration instructions.

Next, the extension was just... abandoned. This usually happens when you have a use case you want the extension to solve, but that use case is so uncommon that there isn't a large enough audience to build a community around the extension. Then the author changes jobs, migrates their codebase, or otherwise just doesn't need to update the extension for their own needs. In this case, you get to decide if you want to remove the functionality from your project, find a replacement extension, or fork the one you're already using. Beware forking projects like this - you're committing to updating for every Django release you upgrade to in the future. I even had one case where I forked an extension only to find that the original author had passed away unexpectedly and there was in fact demand for it. I ended up managing PRs on that one for a couple of years until I was able to find and vet a couple other users to bring on board.

Finally, there are those that are dying a slow death. This could be due to lack of interest, but it seems to usually be because another extension surpassed it in terms of popularity that serves the same function.

The !fun! part is that you can't easily tell which category a dependency is going to fall into until you dig in far enough to find the project's current status. Best case, a quick trip to PyPI and I've got the dependency updated to a newer release in <10m. Worst case I end up having to trace the project's history across multiple git repositories, bug trackers, wikis, various git branches and public forks, &c while I'm walking through the exceptions raised after I've tried to use the outdated extension with a newer version of Django than it was developed against.

Last time around I ran into a group of dependencies that were all related to our implementation of a GraphQL federated subgraph. That took me a full week to untangle. By the time I was done I'd had to fork and update two libraries, got a PR merged and released on a third, and had to rip out two more. Oh, and because one of those dependencies deprecated and removed support for custom GraphQL backends for serialization, I also had to write a regex-based bash script to transform the result of the default schema generation so it would be compatible with the older version of the GraphQL router that we were using. That was a nasty enough hack that I put an intentional "time bomb" in the script so it would fail loudly if the script hadn't been updated in more than a month - then assigned that alert to the team responsible for the GraphQL router upgrade in the hopes that the (intentional) pain of having to update that file every other sprint would encourage them to prioritize that upgrade, as that service was two years out of date.