Herding Ponies: Coordinating and Automating Django Upgrades Across 100+ Repositories

This video features Jeremy Bowman at DjangoCon US 2021 in Online.

Herding Ponies: Coordinating and Automating Django Upgrades Across 100+ Repositories
0:41:06
Published October 20, 2021
758 views

Learn how the Open edX project consistently keeps 14 services spanning 100+ code repositories and multiple organizations on actively supported Django releases! Now on its 4th major Django upgrade, edX has developed an assortment of tools and processes to make these upgrades go faster and smoother.

This talk was presented at: https://2021.djangocon.us/talks/herding-ponies-coordinating-and-django/

LINKS:
Follow Jeremy Bowman 👇
On GitHub: https://github.com/jmbowman

Follow DjangCon US 👇
https://twitter.com/djangocon

Follow DEFNA 👇
https://twitter.com/defnado
https://www.defna.org/

Video production by the speaker and DjangoCon US 2021 Volunteers.

Summary

Jeremy Bowman explains how Open edX manages Django upgrades across more than 100 repositories and over 1.3 million lines of Python. He outlines the normal upgrade process—reading release notes, addressing deprecations, updating dependencies, testing intermediate versions, and deploying—and shows why Open edX’s long-term-support-to-long-term-support schedule makes this difficult. His practical solution is to automate repetitive work with codemods, CI configuration refactoring, structured deprecation reports, dependency-status tracking, scheduled dependency updates, and progress dashboards, while starting early and coordinating with maintainers. He argues that the wider Django ecosystem could make upgrades easier through accurate Django version classifiers, more comprehensive and timely codemods, dependency health dashboards, maintenance services, and tools that generate upgrade pull requests.

Key takeaways

  • Open edX’s Django upgrades span more than 100 repositories, multiple services, millions of lines of Python, and many independently maintained dependencies.
  • A safe upgrade requires good test coverage, reading release notes, enabling and fixing relevant deprecation warnings, updating dependencies, testing intermediate Django versions, and adjusting CI.
  • Codemods are worthwhile when the same judgment-free change appears in many places; existing tools such as Django Upgrade, Django Codemod, Bowler, and LibCST can reduce manual work.
  • Large test suites need structured warning analysis that groups repeated warnings by type, frequency, source location, and relevance to the target Django version.
  • Teams should enumerate Django-using dependencies, track their support status, contact maintainers early, and use dashboards to monitor repository and dependency progress.
  • The Django community could improve upgrades with accurate PyPI classifiers, shared codemods for Django and related packages, dependency health dashboards, and automated upgrade pull requests.

Summarised automatically from the transcript.

Chapters

  1. 0:00 Introduction and Agenda Jeremy Bowman introduces the talk and outlines the challenges and tools involved in coordinating Django upgrades.
  2. 2:05 The Django Upgrade Process An overview of Django’s release cadence and the testing, deprecation-warning, dependency, and deployment steps in a typical upgrade.
  3. 6:52 Open edX at Scale The speaker explains the size, architecture, release constraints, and organizational challenges of upgrading Django across Open edX.
  4. 10:08 Automated Code Refactoring Code mods, including Django Upgrade and Bowler-based tools, are presented as a way to apply repeatable changes across large codebases.
  5. 14:50 Continuous Integration Configuration The talk covers automating updates to GitHub Actions, Travis CI, and tox test matrices across many repositories.
  6. 17:12 Deprecation Warning Analysis A warning-analysis workflow groups and summarizes thousands of test warnings so Django-specific issues can be identified efficiently.
  7. 20:21 Dependency Compatibility Tracking The speaker describes how to enumerate Django-dependent packages and determine whether each supports the target Django version.
  8. 27:28 Upgrade Planning and Automation Strategies for completing the upgrade on schedule include routine dependency updates, early maintainer outreach, and running automation first.
  9. 30:36 Progress Dashboards Open edX’s spreadsheets and project tracking pages are used to monitor dependency support, code changes, blockers, and overall upgrade progress.
  10. 32:56 Improving the Django Upgrade Ecosystem The talk concludes with ideas for better package metadata, broader code mods, automated pull requests, maintenance aids, and dependency health dashboards.

Transcript

7,360 words · auto-generated Show

Automatically transcribed, so expect mistakes in names and technical terms.

0:30

Hello all and welcome to Herding Ponies, coordinating and automating Django upgrades across 100 Plus repositories at DjangoCon US 2021. We have a pretty packed agenda today, so let's dive right into things. First of all, a few words about the person who's currently talking to you. My name is Jeremy Bowman. I'm a manager of engineering at edX. I've been using Python for 21 years now, since about version 1. 5. 2. I've been using Django for 11 years, starting I think around version 1. 2 And I've been through four pretty large Django upgrade projects moving fairly large code bases from one long-term support version to another. I won't say much more about edX right now because we're going to touch more on that when we discuss later what the OpenEDX

1:16

project is and why it's a good example of a fairly large Django upgrade. We're going to go through the following items. First, I'll be going through the process of upgrading Django from one version to another. and then give an example of upgrading a fairly large Django project, namely OpenEDX, and some tools you can use to streamline the process a bit using automated code refactoring and test configuration updates. And then we'll also talk about a tool for analyzing deprecation warnings and categorizing them and why you might want to do this instead of just fixing them one at a time as they appear in your test suite. We'll also talk a bit about how you track the status of the dependencies of your project, which themselves also use Django, and how you can actually get this done in

2:05

a period of time where you can meet whatever deadline you've set for yourself for doing your Django upgrade. And I'll end with a few notes on how as a community we hit make it this even easier in the future uh for not just open edX of course but for anyone upgrading from one version of Django to another So to begin with, the Django upgrade process itself. This will be an introduction to some of you who are relatively new to the project and probably a recap for people who've already been through this process once or twice. The Django project releases new versions every eight months approximately, and most of these are supported for 16 months. So two most of these releases are only supported For about like eight months after the next release comes out. However, once every two years, there's a long-term support or LTS release.

2:55

Which is supported for three years. And this is here primarily for the benefit of organizations that don't want to upgrade Jingrove so frequently. They want to get onto it once and not have to worry about it again for a while. And also may need that one year over uh gap between when the what next LTS version comes out and the previous one reaches end of life to actually get their upgrade accomplished. Spoiler alert, Open edX takes this long-term support to long-term support , release cadence, and we'll go a bit about how that works later on. So if you follow the documentation from the Django project and upgrade a package or a service by hand, your process is going to look something like the following

3:41

First of all, you should have some automated tests with relatively good code coverage so that you can run your test suite and it'll highlight any problems that you're likely to have with the new version before you actually try rolling it out to production. You should then read the release notes for the new Django version you're really upgrading to and take note of any breaking changes that you should probably want to accommodate for in your code, beyond things that just obviously break when you run the tests. You'll also need to upgrade most of your dependencies, which themselves also use Django, to make sure that you've got a version of those which has been updated to work with the latest version of the code. You will also want to make sure that your test suite is showing you deprecation warnings.

4:27

In mini test suites, this is off by default, so you'll want to turn it on. I won't speak to that here, but there are some notes on this in the Django documentation, which I'll link to below. Then you want to actually fix those deprecation warnings that you're seeing, at least the ones that are coming from Django itself that say Hey, this feature which you're using is going to be removed in this Django version. You should make sure to stop using it before you upgrade to that version. Once you've done that, you've fixed all the Django-related deprecation warnings relevant to the version you plan to upgrade to. Then you start testing with that next version. It's possible you may be going multiple versions. For example, if you're going from one LTS to another, there will be probably two major versions in between and you probably want to test with each of those just to catch any new deprecation warnings that may have been added along the way

5:18

that didn't appear in the version you've been using, but are for things that are going to be removed in that next release. Django tries not to give you short quotes like that but it's been known to happen on occasion. Once you've started testing with the new version, you need to fit obviously fix any tests that are failing with the new version. And as I said, you may have found some new deprecation warnings that need to be addressed. You don't necessarily need to fix ones for versions past the one you're upgrading to just yet, but you might want to do so if it's convenient at this time. And finally, you actually release your package or deploy your site with the new version of Django. After this is all done and you've happily upgraded to a new version of Django, at some point in the future you can stop testing with the prior versions of Django

6:06

and clean up your CI matrix a bit. And here's the promised link to the Django documentation on how to actually do the upgrade. And note that This can all be done in parallel with other coding work and releases. You don't need to stop everything and say we're just doing a Django upgrade now. You can do this on your main development branch as you're making other changes incrementally And there's typically nothing here that requires major interruptions to the rest of your development other than just taking a bit of development time. So that looks pretty straightforward. How hard could it possibly be? Well, let's see when you try doing what happens when you try doing this in a much larger than most Django project.

6:52

So first, what is OpenEX? It's the open source learning platform that powers edX. org and thousands of other sites. So it's a learning management system that has all kinds of functionality for helping people learn. The codebase is primarily licensed under the AGPL version 3 and Apache version 2. Most of it is Python and JavaScript, although there is a smattering of other languages in there. For Python specifically, there are over 1. 3 million lines of Python code scattered across more than 100 GitHub repositories. So this is already looking a little larger than your typical Django project. It uses Django for most of its services. Note I said plural. There's not just one service in OpenEX, it's a collection of more than a dozen different services working together to provide that learning platform.

7:44

The project produces supported open source releases about every six months, which are used by those thousands of sites I mentioned to run their sites. and try to provide these at times which are relatively convenient for the academic calendar. And edX, the organization, provides support for these releases for six months after release. And you can find more about OpenEX at open. edx. org and in the edX GitHub repository. So When we try upgrading this, how does that look like? Well, as I said, we stay on the long-term support releases, and we actually need to finish this upgrade. . eight months before the current Django long-term support release hits end of life.

8:30

Why is that? Well because we need to test the new code for about two months with all the with a number of those people who are running other sites And then we're supporting it for six months. So we don't want the people who are counting on being able to run the software for six months suddenly stopping to get security patches for Django partway into that support window So we really need the code done and ready to test eight months before the software hit. The Django release its end of life I mentioned before there's a lot of code in OpenEX and that's enough to run a foul of many of Django's breaking changes. We don't hit all of them because there's a lot of things that change, little dark corners of Django here and there, but we hit a lot of them And I'd say that there's over 100 repositories to update. It's not like we just update one CI suite, fix some deprecation warnings, and we're done.

9:19

In addition, these Repositories are owned by different teams who also have competing priorities. There are other things that they're working on, which are very important, sometimes seeming more important to them than getting this Django upgrade done. And OpenIS relies on more than a hundred packages from PyPI that themselves use Django. Many of these, by that time eight months before the window, haven't actually tested against the new Django long-term support really. Yeah. Typically a few of these have actually been recently abandoned. No one's working on them anymore. This becomes a forcing function to deal with problematic upgrades that we've deferred working on. You said, oh well, this is a breaking change. We don't want to deal with that right now. I'll come back to it later. Later is often, okay, well we now need to upgrade it because the version we're using doesn't have support for the new Django version.

10:08

We actually need to fix this now. And we can't afford to skip or defer upgrades because security patches for Django itself. There are typically developer productivity improvements in here that make OpenIX developers more productive. And so following this process manually, we did this the first couple of times, takes more than five developers four to six months. And give it how many other things we do and how much we would like those developers to be accomplishing that's not just upgrading Django, we decided to start upgrading operating parts of this. So let's talk about the first of those. Automating code refactoring. When you need to make the same changes across millions of lines of code, or at least hundreds of thousands.

10:55

So what's a code mod? I'll be talking about this a lot. My definition of this abbreviation for this is automated source code modification or refactoring. That's not an official definition, but it was a term that seems to have been coined by this project at Facebook about thirteen years ago. If you know of any earlier reference to this term, please let me know, but I think they just picked codemod. py as their file and the name stuck. There are lots of tools that do this. They'll like PyUpgrade, Black, and I sort, etc. They will run a script on your code and produce a new version of the code that's a little cleaner than it was before in some way. There are frameworks to write your own code modification scripts. Some of these are Bowler, libcst, pasta, red

11:43

baron, etc. OpenEDX started using Bowler a little over two years ago. It's been working pretty well for us. We haven't revisited the choice recently. We may do that in the near future. When do you want to write code to automate fixing other code? Well, one is when you need to make the same change in dozens of places. If you're just making affix like one or two different places in your code, it's faster to just make the change and not waste your time trying to automate it because it takes a little time to write these. when the change doesn't require a judgment call. So if it's like, oh I found this thing that you now need to provide more information for it to work correctly, that's not a good place for something to automate and just

12:28

do the right thing on its own. You actually need to make a decision there. But you can definitely automate the ones where it's just the name of the function changed, for example. And when the effort to automate is reasonably low. If it doesn't take you long to do the refactoring, sure, then you might want to do that even if you need to finish it in like a dozen places, but it's in multiple repositories. Versus if it's gonna be really hard to figure out how to automate this thing correctly, it's kind of a complicated change, you may still be better off doing it by hand And that last point is really key. The easier it is to automate refactoring something, the more often it makes sense to actually do that automation. So the easiest case here is use existing code mods. Why reim at the wheel if someone's already done this? Here are a couple of projects that already exist

13:15

that do this. Django CardBot and Django Upgrade. They are designed to help other projects make some of the backwards and compatible changes that have occurred in Django over time and apply that change to your code base. And I've also provided a link to some of these tools that edX created using Bowler. I'm not sure that's as good a starting point for others, but it has some things in here that may not have been in the previous two. Also, I've noticed that there's recently added a grid on Gjenko packages for such tools. It only has these two right now, but you may want to keep an eye on it in case someone adds a new project later. With a little more effort, you can create your own encode mod. And this is actually easier than you might suspect, especially if you're using the right. framework. So for example in the Django CodeMod project they have a utility class called Base

14:04

Funk Rename Transformer which handles Hey, this function renamed from one to another, and it handles all the complicated parts of how to find all the usages, tell whether it's a call or a definition, etc. And it's actually coming from Django versus something else and it handles that for you. So depending on which framework you're using and what kind of refactoring you're trying to implement, this can actually be pretty quick to accomplish. As for bonus points, you can automate the automation. At edX, we actually created a Jenkins job, which takes a list of repositories, a version of Python to run. a set of packages to install and then a bash script, which is typically just to call Python with a set of parameters, to

14:50

actually make uh modifications in one or more repositories, generate pull requests for them, and automatically kick off the test suites. This has saved us a lot of time in setting up a local development environment for each of these different repos we're actually wanting to change, especially if it's just we're wanting to run some automation across each of them. And if you don't have Jenkins set up, there's either other ways you can do this using assorted scripting tools. So that's automating code refactoring. Now what about the actual definition of your test configuration? Again, if you just have one project, you're probably just going to do this by hand. But if you have more than a few repositories, this actually starts taking some time to update.

15:37

So here's an example of what you might be doing in such a project. Okay, well you add Django 3. 1 and Django 3. 2 entries in your say GitHub Actions definition and go up and update your Tux. ini file to add the new entry. Okay, well in this project you'd already been testing 3. 1 in TOX but not in GitHub Actions So just add the missing 3. 2 entry and make sure you've got a condition to set that correctly. That's not too hard, but you've noted that we've already changed code in three different places and they kind of need to match. And it's just complicated enough. It's not hard, but it's easy to forget one of these steps. And we do this a lot. Like I said, we're talking about over 100 repositories here. And we're running other refactoring scripts anyway.

16:24

So why don't we just automate this? So You note that the GitHub Actions used YAML files. Travis does similar, and some of the a lot of other tools in the space do as well. The role map. yaml package is great for this. It preserves comments and the order of lists and produces something's output which is very similar to the input just with the changes you've specified. At edX, we use this to automatically add or remove Django versions from the test matrix. And we wrote scripts to do this for GitHub Actions and Travis CI because those are the two systems we currently use for our continuous integration. And an extra point on this is it encourages to actually keep these files consistent. We noticed that these were drifting a little bit in terms of how they were set up and what the spacing was.

17:12

And it was making it hard to copy content from one to another. And actually using automation of these files encouraged us to actually be a little more consistent about the way we format them. And in this directory of one of our repositories, you can find a script for doing this refactoring of the CI files for the Django 3. 2 long-term support release. Similarly, refactoring talks to INI is basically just an INI file. Python's config parser class works for this. edx order script for this too, and you'll find it in the same folder. Now, moving on to something really the test suite, analyzing deprecation warnings. What do you do when you have more than a thousand of them?

17:58

If you're fixing ten of these, yeah sure, just go through and fix them by hand. But If there's more, you may want to take a but a more sophisticated strategy to this. It's kind of like looking for a needle in a haystack when you're trying to deal with a case like this. So If you haven't dealt with looking at deprecation warnings on a large project before, warnings are repeated for each test case they occur in. So you might see the same warning occur multiple times because you hit the same code in multiple tests. You may have pre-existing warnings for things that are unrelated to Django. It's not just deprecation warnings from Django that show up in the test suite. It's deprecation warnings from other packages and other types of warnings like runtime warnings or resource warnings. And

18:44

If you've got hundreds or thousands of these things at a list, it's hard to tell which one's happening often enough to be worth scripting them. For example, from one CI run on edX platform. There is this snippet, you've got a couple of resource warnings, and then, oh yeah, look, there's a removed in Django 3. 0 warning. That one actually needs attention for this project, but it's kind of hard to spot that among thousands of lines of Jenkins output in this case. So we created something that looks more like this. It looks much more useful. This actually categorizes the warnings in the test suite by the type of warning, and you can drill down and see. like what section of the code it was in and what the name of the warning is, how many times it was encountered, and then specifically

19:32

which lines of code triggered the warning. And this helps you find just the warnings relevant to Django deprecation that we need to fix for this project. How did we manage to do that? First of all, we use the PyTest JSON report package to get the list of warnings in a parsable format. If you're using PyTest by default, it just comes out as text in the console and that's a little hard to work with. Used iter tools like group by to take that JSON data once parked in the Python dictionaries and group it together by similar warnings. And then do some HTML generation with a little bit of CSS to make it possible to expand and collapse sections without resorting to JavaScript. And then we just attach this generated HTML file to the CI run.

20:21

The code for this is currently built into the edX platform repository, which is the largest code repository in OpenNedX. But we did start to break it out into a separate installable package because we'd like to use this in our other repositories. We think other people would find it useful also. But as of the time I'm speaking to you about this, we haven't finished that refactoring work. We would like to do so, but if anyone else is eager to use this, we'd certainly appreciate any assistance we can get in finishing that up. And then we get into dependency status tracking. So we've talked a lot about updating your own code to work with the new version of Django you're upgrading to. But then you're also using other people's code. So how many, you know, in the the case of OpenedX, dozens of external packages haven't even started the upgrade that I need to be done with in about two months?

21:13

So let's talk about this. Why is this hard? As I said earlier, some packages over the span of two years since you last did a major upgrade for a long-term support release. They've been abandoned. They were being maintained by one or two people. They've switched jobs or moved on to other things, and they just aren't working on it anymore. And maybe there weren't many other users, and the fate of the package is now in limbo. This happens occasionally, especially if you're using less popular packages. Some of them just haven't been updated yet. Remember, OpenEX does this eight months before the end of life of the previous Django long-term support release. Sometimes the code has been updated. It's on the main development branch, but it hasn't been released yet, which means that if you want to get those changes

22:01

need to update your dependency files to go from a PyPI release to installing it from GitHub, which has a sort of drawbacks. Sometimes the fix was released. Yay, but they made breaking changes along with it, and you haven't accommodated for those in your project yet. So this is going to take you a little while. Sometimes they dropped support for the previous Django version you were using before they added support for the new one or concurrently with it. The Django project strongly discourages this, but that doesn't mean that projects don't do it once in a while when they view the overhead of supporting two disparate versions concurrently as being too high. And it takes time to figure out for each dependency you have which of these is true.

22:47

You may not have a list of all the dependencies you have that use Jenko. If you just are tracking this on an ad hoc basis per project, It may take a little while to go through and put together the set of like, okay, well of all these Python dependencies I'm using, which ones are using Django? And actually what are all the things I'm using? I may state my direct dependencies, but how do I know what things those packages depend on? Do I have those enumerated? Or you may have the opposite problem. You have dozens of lists of Django using dependencies because you have a separate list in each repository. So the first thing to do here is just enumerate your dependencies that depend on Django. At edX in the Open EdX project, we use pip tools, but you can also use Poetry or PipEmp or something like that First get enumerate all of your dependencies

23:34

and aggregate that list across all of your repositories. So if you're doing more than one or two repos, it's nice to actually have a master list you can work from because Often they'll be sharing some of the same packages and you don't want to repeat the work in getting them each updated. And then you need to look through those dependencies and figure out which ones are using Django. Conveniently, there are trove classifiers supported on PyPI for support for use of Django of a particular version. Unfortunately, not all projects actually utilize these, so you may also need to look at the install requires from setup. py rcfg. You may need to look at the package name. If the package name includes the word Django, there's a good chance it's meant to work with Django and needs to be tested against each Django. version and so forth.

24:21

Open edX uses pip tools for this. We also have a dependency management standard to that stipulates how we manage our requirements files in a way that's conducive to managing upgrades and maintenance. And we have a code base for running a cross-repository metrics collection. So this is the standard for our requirements file management, and this is the code for our you know job which generates uh this is a small portion of the data but it for each of the repositories it says this is the set of dependencies of that project which are known to use Django No. The edX repo health repository is uh specific to uh

25:06

edX repository the OpenEX project, but there's a lot of general useful things there. It's actually built on another project called PyTest Repo Health, which is intended to be general use. But we haven't really plugged it with the community yet. But if any of you are interested in creating health dashboards for your project, I think that's a very good place to start and would love to make that more widely useful to the Python and Django communities. And then you actually need to figure out for each of these what is the Django version support status. So you're going to want something to track this in, probably a spreadsheet or wiki, because unfortunately it's hard to do this all programmatic at this point. So you can try those Trif classifiers I mentioned. You can read the release notes or changelog for the project and see, you know, does it mention like, hey, added support for Django

25:54

version 3. 1 in this release. You can check the continuous integration run for the latest uh release commit. So if the it doesn't the package doesn't have a change log or They've neglected to mention what was in the last release. You can look in CI and see, well, did they test the version that I'm looking for and did it pass? If you can't find that, if that didn't work, well then you can check well on the main development branch, have they added support for the version you want there, even if it's not in the latest release? Okay, well, it's not even being tested on the development branch yet. Now you can look for open pull requests to see if someone's said, hey, here's some code to add support for this Django version, and maybe it's just held up in the review process somewhere. If there's not even a pull request submitting this and there's not much activity on the repository yet

26:43

yet, currently you may want to look for a more active fork of the project and see if this has just been abandoned and someone else has taken it and run with it but they needed to rename or put it in a new repository. You can look, you know, for example, in the GitHub network graph to get some indication of other forks that may be more actively maintained. Um you can if there if it looks like the thing is still maintained, it just doesn't have support yet, you can submit a PR yourself to add that support. And if all else fails, well, when you've created that PR yourself to add support for it, if that you never hear back from the maintainer, you don't have time to see if anyone else is interested in maintaining the package. You can just use that fork you've created, install from

27:28

GitHub or GitLab or whatever repository system you're using, and just rely on that for the time being until you can come up with a long-term fix later. You want to start this process early because maintainers may need time to respond to you. If you're submitting a PR or if you're asking for a release. They may have other life priorities and or just need time to think about it and work at it. And they may be not be able to turn around very quickly. So the earlier you start this the better your odds of getting a reply and a useful reaction by the time you actually need to finish your upgrade So how do you get this done on time? Because now it's starting to look pretty complicated. And as I said, we don't want this taking four to six months of an entire team's effort.

28:13

First thing is routinely upgrade your dependencies. The OpenEX project uses scheduled GitHub actions runs the pip compile tool from the pip tools project and automatically generates a PR. Most of the repositories are scheduled to do this weekly And here I have the link to the GitHub Action that we use to do that. And it links itself to other code, which is also open source for how we run pip compile and how we generate those pull requests. But it's not that difficult to switch this if you're using other system like poetry or tip env, etc. When you do need to put a dependency to a specific version because say there was a breaking change or there's a bug in the new version and you can't afford to upgrade right now, make sure to revisit that before you need to get around to upgrading Django

29:03

again. You don't want to have to be facing your entire backlog of deferred upgrades when you're also trying to fix all the issues with supporting the new version of Chenko. That's just a recipe for disaster. To help you do that, you always want to add a comment, at least if you're using requirements files, linking to whatever issue must be resolved before you can unpin that dependency. Because it's also terrible to come back even a week or a month after the fact and try to think, wait, why was this pinned? especially if someone else in your organization did it. You want to run all the automated stuff first. Don't get bogged down into fixing specific deprecation warnings and trying to figure out why that test failed, if there are still things you can run to

29:50

do code refactoring or CI test You see I matrix updates etc. Anything you can fix automatically just run all of that first because it'll automatically fix some of those things that you'd otherwise otherwise waste time debugging. And this will also get you a feel for how much scope of effort is left in terms of getting your dependencies updated and getting the warnings fixed. So you can figure out what level of resourcing you actually going to need in order to fix this. You won't get an exact number, of course, but it'll give you a much better feel for it. And As I said, you want to get in touch with the dependency maintainers early for your dependencies and offer to help them. This will give them much more time and incentive to try to help resolve

30:36

whatever issues you may have with the latest Django releases in time for you to finish your upgrade And finally, you want to create a dashboard to track your progress. As it the project's just getting larger, it gets kind of hard to keep track of across dozens or hundreds of repositories and all these lines of code and dependencies and everything. You want something that actually keep tabs on where you are in this. That is probably a little bit more sophisticated than just an issue tracker because an issue tracker is it's going to tell you where you are but it's not going to show you a good glimpse of the overall picture So for example, the OpenIS project uses a Google Sheet to track the state of all of our Django using dependencies.

31:23

We put this together a little bit before we started the 3. 2 upgrade. uh process and we have this now showing all of our dependencies using Django. Have they been explicitly tested for support with 3. 0 yet? 3-1, 3-2. When did we last check to see if it's supported so we know if it's time to go back and take another look? And if we've created an issue for figuring out what to do with the dependency, go ahead and link that here. Here's a link to the sheet. This is actually public. You obviously don't want to use this as is, but you may want to copy over the format and or look at some of the We've got other tabs in here for generating a pie chart for a total percentage completed of how far we are along the upgrade, and some of that may be useful for you. And then for the overall project, you go on the dashboard for that also, not just the

32:09

okay, where are we getting the dependency update? Because even after you've updated all of your dependencies to versions that work with the Django version you're upgrading to, you still need to upgrade your own code. So we have this in a confluence wiki and these are the steps that we've called out that need to be done. So we want to make sure that for each thing we've applied any available code mods, we've updated the configuous integration and testing matrices. We fixed any available deprecation warnings that are showing up in the test suite, and what is currently blocking us from making further project progress on that particular part of the project. And that blocker changes over time as tickets get finished. But it lets us get a glance of like, okay, well, what's currently holding us back from getting more done on this?

32:56

And again, here's the link to the conference page for that. That's available for you to look at in case it's useful. So, how can we make this even easier moving forward? So there was a lot here, and we've automated a good chunk of this for OpenEDX, but each time we do it, we think of new ways that this could be even better the next time around. So here are some suggestions from where we are in our current upgraded iteration of things that could make this even better, some of which are beyond the ability of the Open Edicks project to do on its own, but require some broader community participation, which is one of the reasons I'm giving this talk and reaching out to you now, so maybe we can work on this a bit better as a community to make this even easier. So here are some ideas for improvement.

33:41

First of all, making sure that more of the packages using Django are correctly using the Django trove classifiers. Automation works much better if the release and the development branch say explicitly in the configuration, these are the versions of Django that this code uses, and in fact this package does use Django. If that's not there, you have to go through a lot more work to get that information. Then the jjanglepackages. org site is wonderful. It's a great index of information about various packages available for Django, it would be great if it actually had this information in it. So you could say, I'm working on a project that currently uses Django 3. 0. What packages are available that state that they explicitly state that they work with Django

34:27

3. 0? That would miss some packages that may happen to work with but haven't declared it. but it'll be a good guideline for like where to start with. And again, if these classifiers are on PI PyPI, this could be automated and just pull it down just for the packages that actually have that. And it would just be blank for the others. Create more upgrade automation code mods and release them sooner. I was heartened to see when preparing this presentation that two projects for doing this have been started recently. and have a pretty good range of fixers for the individual changes made in recent Django upgrades But it would be great if those were even more comprehensive and if they were released very close to the time that the Django releases are actually made.

35:12

This would make it much easier for the package ecosystem to do their upgrades if a lot of the work was just automated for them. Similarly it'd be good to see code mods just for the not just for the Django breaking changes, but for other packages breaking changes Because things like Django Rust Framework and Django CMS and Django Storages and so forth, those also have breaking changes periodically, some of which could be automated with code mods to fix them. And if we had a repository for those, that would make it easier to do some of the other package upgrades that you need to do in order to get a set of dependencies that work with the version of Jenker you're upgrading to. Make it even easier to write the new code bots. As I said, the Django upgrade project seems to have a pretty good basis for this, but there may be even further room for improvement.

36:05

um making detectors for breaking changes that aren't easy to write code mods for. Like I said, there's a category of changes That you can't really automate the fix for because you have to make some decision about how to handle it. But maybe you can just detect that, oh hey, you're using this thing. Which is changing, you might want to change that. Now you may say, well, isn't that what deprecation warnings are for? And to a large extent, yes, but keep in mind that that only works if you have a test suite with good test coverage There's a lot of projects that unfortunately don't have much of a test suite or don't have very good coverage for the test suite they have, and they need to do upgrades also, and they may not be able to rely on deprecation warnings showing up for all of the places in their code that they're using things that changed.

36:54

If there was something that was half of the code model, just the detection part that would warn them, hey, you're using this thing here, you should fix it, that would make the process work substantially better. I mentioned a few projects we've done at Open EdX that have been useful for us for updating GitHub Actions, Travis CI, and the warnings analyzer script. We've started packaging those up for broader adoption, but we'd like to finish doing that. And if anyone wants to help with that, please get in touch. We'd love to hear from you. And there are all these different separate steps of automation, but it'd be wonderful just to have a script that's like generate a PR to upgrade everything that can be automated for the upgrade you're attempting to do. And this would take a bit of work, but if you could point the script at a repo and detect, is it using GitHub Actions?

37:42

Is it using Travis? Okay, figure out which one. Apply the CI changes. Now, okay, it's you're currently using this Django version. You said in the command line that you're upgrading to this version, I'm going to go find all the code mods I have that work for that This would especially make it useful for submitting PRs to packages where the maintainers don't have a lot of time on their hands to go and deal with this on a regular basis. and just generate on APR. It's like, hey, here's this thing that upgrades your project to mostly work with the new version. Please just double check it and merge it. And on that note, it might be useful if the community started using more maintenance aids like Jazz Band and Tide Lift.

38:27

And I'm not going to plug just these specific two. There's other things like this also, but There's a number of ways out there that it can be easier to maintain a project. And if we use these more often, maybe would be slightly less often hitting this. case where a package has been abandoned or nearly abandoned because the maintainer is overwhelmed and it's a single person. And you have to have a dashboard generator for the Django support status of the project's dependencies, even if it's only looking at the trove classifiers, which is known to be incomplete. It could just spit out the result of that. Again, PyTest Repo Health might be a good starting point for this. That's something we'll probably try for our next upgrade project if no one else has done it by then. But if someone wants to help us with this, would love to take a stab at that earlier.

39:16

So I've been talking about collaboration. How do we go about that? OpenIS developers are eventually going to pursue some of these ideas. But probably not all of them. Some of them are beyond the scope of what we could accomplish just in the OpenEDX project. Some of them require assistance from maintainers of other packages, and some of them we just don't have the developer resources to accomplish. And we're currently wrapping up our Django 3. 2 upgrade process, so we might not be getting to this again soon as Django upgrades take a back seat until we get to an eventual 4. 2 release. We would love to work together with other people on this. Would love to hear from you if you're interested on building on some of the things we've done, or If you've come up with any other good projects or ideas that we overlooked in the process of doing this, I'd love to hear that there are things out there that make this even easier that already exist that I just haven't heard about.

40:09

This is my email address if you want to reach out to me specifically. And these are the discussion forums and Slack workspace at this link. You can reach me there and also other members of the OpenMedics community. Thank you very much, and if you have any questions, I'll take them in the chat.

Questions this talk answers

How do you plan a Django LTS-to-LTS upgrade for a large project?

Open edX starts early because it needs the upgrade ready about eight months before the previous Django LTS reaches end of life: roughly two months for testing with users and six months of supported releases. Its scale—over 100 repositories, many teams, and more than 100 Django-dependent packages—means the work must be coordinated rather than handled as a single-project upgrade.

Discussed at 8:30

When should I automate a code refactoring instead of doing it by hand?

Automate changes that occur in dozens of places, do not require judgment, and can be implemented at a reasonable cost. For isolated changes or changes requiring decisions about the correct behavior, manual editing is usually better.

Discussed at 11:43

What tools can automate Django upgrade code changes?

Existing tools include Django Codemod and Django Upgrade; projects can also write custom codemods with frameworks such as Bowler, LibCST, Pasta, or RedBaron. Open edX uses Bowler and runs scripts across repositories through Jenkins, generating pull requests and starting their test suites automatically.

Discussed at 13:15

How can I automatically update Django versions in CI configuration?

Use structured-file tools to modify the CI configuration rather than editing every file manually. Open edX uses ruamel.yaml for GitHub Actions and Travis CI YAML files, and Python's configparser for tox.ini, allowing Django versions to be added or removed consistently across repositories.

Discussed at 16:24

How do you deal with thousands of Django deprecation warnings?

Instead of inspecting raw test output, collect warnings in a machine-readable format with pytest-json-report, group similar warnings, and generate an HTML report showing warning types, counts, affected code, and triggering lines. This makes Django-specific warnings easier to prioritize among unrelated warnings and repeated occurrences.

Discussed at 17:58

How do I find which dependencies need updating for a Django upgrade?

Enumerate dependencies across all repositories and aggregate them into a master list, using tools such as pip-tools, Poetry, or Pipenv. Identify which packages use Django through PyPI classifiers, install requirements, or package names, then track their support status centrally rather than investigating duplicates repository by repository.

Discussed at 22:47

How can I determine whether a dependency supports the Django version I am upgrading to?

Check its PyPI classifiers, changelog, and CI results for the latest release; then inspect the development branch and open pull requests if necessary. If support is still missing, submit a patch, use an actively maintained fork, or temporarily install your own fork from GitHub or GitLab.

Discussed at 25:06

How can I finish a large Django upgrade on schedule?

Upgrade dependencies routinely with automated dependency-update jobs, document reasons for pinned versions, run all available codemods and CI-matrix updates before debugging individual failures, contact dependency maintainers early, and track repository and dependency progress on a dashboard. These steps reduce the backlog and reveal the remaining scope and resource needs.

Discussed at 28:13

Presenters

Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.

More videos from DjangoCon US