Please forgive the potentially inflammatory title. This is something that’s been on my mind for a while, but I haven’t had time to really think through a decent proposal around the specifics. I still don’t have that time, but I wanted to get the topic out there in case others want to pursue it (or tell me I’m crazy).
To be clear up front, I still believe:
- Django Plugins were a good thing that has significantly advanced our extensibility story and made the best of the situation we had.
- Hooks (events + filters) are a good thing period, and would continue to exist in some form regardless of anything proposed below.
- You absolutely should be using these mechanisms to extend edx-platform functionality today.
Our Plugins vs. Django Conventions
A reusable Django app is built with certain conventions that make it easy to pip install and include them into your own Django project, potentially alongside your own apps. Your project defines some top level URLs configuration and then delegates certain directories to be handled by those different apps. But the ultimate control comes from your project file.
The edx-platform repo contains a whole constellation of apps. Many of them are built using conventions that make them theoretically reusable, but the deployment process as a whole assumes that edx-platform is the only thing running. You don’t have a project and load pip-installed edx-platform apps into it–you run edx-platform itself, use its project files, and tell it where to find the configuration values that will customize it to your site. That’s the way it organically grew in the early days, because the overriding priority for edX at the time was to have a functioning site, and the extensibility use case wasn’t our highest priority at the time.
The framework that we created to allow for plugins deals with that situation as best as we could manage. It doesn’t fundamentally change how edx-platform operates or is configured, but instead gives apps a way to inject themselves into the process and dynamically add their apps, URL configuration, etc. It’s apps-as-dependency-injection, rather than apps being explicitly included via a separate project settings file.
Why it matters: Plugin CI
Most plugins are going to call into something in edx-platform–enrollments, course content, scheduling metadata, etc. But these plugins can’t call into edx-platform for real during their unit test runs. So instead, they make do with mocks, or they create filters that are built a certain way and assume that they will be called correctly. Their plugin’s CI runs with a bunch of mocked dependencies, edx-platform runs its own internal CI checks, and then the two get mashed together during deployment.
Regressions can easily slip through due to that post-CI combining of code. Maybe someone touching edx-platform code altered the behavior of a filter pipeline in a way that had unexpected consequences. Maybe the API was extended to handle a certain case when a setting was active, but didn’t properly test what happened when said setting was something else. Maybe there was a change to a model that was never part of any api.py
file, but was so important and had been unchanged for so long that people just started using it anyway–because it was the only way to get the information that they needed.
Whatever the cause, there’s a wide set of failure modes that neither the plugin nor edx-platform have any way to effectively guard against during their CI builds.
What would the alternative look like?
At a high level:
- edx-platform becomes a pip-installable thing.
- There are still settings modules that ship with edx-platform.
- If you have a generally reusable app that extends edx-platform functionality, it can be in its own repo, run its own CI, and have edx-platform as a dependency.
- If you need to extend the platform for your site and have a number of customizations, you would make a repo for those apps, create a project in that new repo, add edx-platform’s default apps into your project along-side your own, and run CI for your apps, treating edx-platform like the big dependency it is.
- We slim down edx-platform’s messy configuration and settings-based app/URL modification to make this sane to work with.
- We figure out how Tutor and other deployment methods could change to make use of this.
The openedx-events repo still exists in this scenario. We still want a central repository for the versioning and documentation of events, as well as the ability to send these events over a message queue to external services that can read them.
Filters still exist. There are places where we really do want dependency injection, e.g. rejecting an enrollment, modifying Unit rendering, etc. That doesn’t change. The main change would be that we could actually exercise those views with our plugins in them.
Stable APIs are still important. This doesn’t obviate the need for stable, documented APIs. API changes in edx-platform can still break plugin code that builds on top of them. But this at least lets us better catch those issues before deployment.
It would eventually be a replacement for Django Plugins as they exist today. But the migration shouldn’t be terrible, since there are direct Django analogs for almost all the things that plugins actually do–map URLs, add apps, configuration, etc.
Why now?
I wanted to bring awareness to this because there are a lot of promising things on the horizon, where we’re looking to simplify configuration and operation, or expand on the hooks framework for extensibility. The extensibility story for Open edX has never been better, and we’re continuing to make significant investments there on both the back and frontends.
At the same time, we’re going to be whittling down more and more of edx-platform. We already have a project going to extract the XBlocks that live in edx-platform into a new repo. Between that and the ever closer switchover to MFEs, edx-platform will likely lose a lot of its static asset bloat.
I think that all of this opens up a window of opportunity for us to do something that would have been far too much effort back when we were first developing our Django plugins infrastructure. I also think this would help address one of the biggest shortcomings of our backend extensibility story, and improve confidence in testing and deployment.