Skip to main content

Conference Session

Rebuilding the LMS Mid-Flight: A Drupal Success Story with AFT and AFL-CIO

September 30, 2026
Photo of Marcin Grabias

Marcin Grabias

Senior Drupal Engineer

The AFT and the AFL-CIO train their members on e-learning sites where a lost course record is not an option. The platform behind those sites started at Tag1 in 2018 on Opigno, and when that foundation stopped fitting, the team rebuilt its engine on Drupal core APIs, released the result as Drupal LMS, and migrated a production site in place with no downtime. Marcin Grabias walks through each decision along the way, including the fork and the migration, so other teams facing an aging distribution can see what a careful rebuild looks like.

Session Description

The AFT and the AFL-CIO run e-learning sites for millions of members, and at that scale there is no room for lost progress or corrupt completion records. This session follows how the platform behind those sites moved from a heavily customized Opigno build to Drupal LMS, a learning management system built on Drupal core APIs, while the sites stayed in production.

Marcin Grabias (Senior Drupal Engineer) traces the project from its 2018 start on the Group module, through forking Opigno and replacing thousands of lines of its code with a compact training manager engine, to the Drupal LMS alpha in 2024 and today's stable 1.2 release. He explains how the migration ran on the live site with no downtime, and why Share My Lesson was built on Drupal LMS from the start.

He also covers how the project is sustained. When a paid project needs something, the work goes upstream into Drupal LMS instead of a private repository, and a community of almost 100 people meets in Slack and in monthly calls. The Q&A closes on how AI grading checks a student's written answer against a teacher's source of truth and responds with hints.

What You Will Learn

  • Why the team rebuilt on Drupal core APIs instead of porting a Drupal 7 era distribution
  • How keeping Opigno's data structure made the move to Drupal LMS easier
  • How isolating the LMS engine made an in-place migration on a live site possible
  • How client work feeds the open source project instead of a private codebase
  • What Drupal LMS offers today (single directory components, revisioning, and anonymous user tracking)
  • How AI grading of written answers works in Drupal LMS

Transcript

[00:00:05] Good morning, everyone. My name is Marcin Grabias. I am a Drupal engineer at Tag1 Consulting, and I started with Drupal 6 and 7, and I absolutely fell in love with Drupal 8 and the next major releases, which, oh, sorry, which gave amazing opportunities, amazing possibilities of development. And today I'm going to show you one of the achievements that uses the API, which is Drupal LMS, and we're going to go through rebuilding the engine of, well, a couple of live sites from our clients.

[00:01:09] Now, let's see what's the plan for the next 45 minutes. So first, the scale and the crucible. I'm going to tell you who are the clients that have real-world, high-stakes e-learning platforms. Then we'll give proper credit to the Opigno distribution, and we'll also stop a bit to wonder why it didn't work in the longer term, with the Drupal 8 conversion impact. And then we'll go through the entire story of converting from Opigno to Drupal LMS mid-flight on live sites. Then a bit of honesty: how Drupal LMS really happened. And then, if time allows, I will show you a bit of advanced Drupal API engineering on some examples. All right.

[00:02:21] Let's go. So first, we have our clients, the American Federation of Teachers and the American Federation of Labor and Congress of Industrial Organizations. Yes, we're in Europe, so I will tell you a bit more about those organizations. Those are basically labor unions. I'd never been in one because of the type of my work, but I learned a bit about them, and it turned out that in Europe they work in a very similar way as in the United States.

[00:03:01] So let's start with AFL-CIO. It's an umbrella federation which is on the national level, and it covers many different labor unions, nearly 15 million members, retired and active workers. Just for a comparison, the agglomeration we are in, the Amsterdam, Rotterdam hub, and there's one more city, has about 8 million people living in it. So that's a very big scale. There's also the AFT, which actually started the first project with Tag1 Consulting, and it has 1.7 million members. It's the American Federation of Teachers. There's a second federation; this one is for major cities and higher education, in comparison with the second one, that's more for rural areas and primary schools and so on.

[00:04:22] Okay. So with that kind of scale, there is no tolerance for lost progress, corrupt completion records, and so on. We really need to do it the right way. And yeah, there are two platforms currently. One is the one that got migrated, which is AFL-CIO Labor Lab, plus other affiliate sites, which also share the same code base. And there's also aftlearning.org, that actually started the entire project. It was only aftlearning.org at the beginning. There's also the Share My Lesson platform on AFT. I worked just a bit on this project, and, well, it's a greenfield, so Drupal LMS was dropped there, I think two years ago, or roughly a bit less, and there was no migration there.

[00:05:34] Okay. Now a bit about Opigno. Well, we owe an enormous debt of gratitude to Opigno, because they are the ones that actually started e-learning on Drupal. It happened in 2008, from Drupal 7, if I'm not mistaken. So that's 15 years of experience. And, well, Opigno has a proven data model. It has learning activities: so courses (in Opigno, learning paths), then lessons (in Opigno, modules), then activities, plus relevant status entities, so course statuses, lesson statuses, and answers. That data model was actually a blueprint for Drupal LMS. There's also the course manager, which offers a course editing experience on one page. Well, almost; it's a React app. So we also rebuilt that, basing on that experience. And it has native Group module integration, which allows you to organize students in classes and so on. So it was a base for Drupal LMS.

[00:07:06] Now, what happened there? In the Drupal 7 era, distributions were very popular. A distribution is like a monolith. It usually has many, many dependencies that make things complicated if we want to remove a piece, for example. So it is like a big bundle. And in modern Drupal, we are moving to composable structures, so modules that can be bundled with recipes. But, well, when developing those smaller pieces, there is never a circular dependency. So if module B depends on module A, then module A doesn't have any code specific to module B. So it's easier to compose those pieces.

[00:08:05] And, well, it's a well-known Drupal evolution pattern. It already happened in the past. So Ubercart and the Kickstart distribution became Drupal Commerce. Well, Drupal Commerce actually took the place. Similarly, Organic Groups were replaced with the Group module by Kristiaan Van den Eynde. There were also distros, which, well, now is covered by Drupal CMS and recipes. There's also the thing with the Drupal 7 to 8 conversion. In the early times, that was a hard task. The API was evolving. So those early projects that got converted to Drupal 8 had a very high risk of failure and issues. And I think that's what happened here. A redesign was needed, and a rewrite, instead of trying to port things over.

[00:09:14] Okay. Now for the story. It will be in the flight theme today. Okay. So the LMS project in Tag1 Consulting started in 2018. It was always based on the Group module. It was like the anchor. And the American Federation of Teachers needed a self-hosted LMS, well, with not a huge budget. So software as a service platforms were avoided, because those usually have per-member fees, which, with that scale of memberships, is out of the question. That would be just too expensive. So, yeah, Opigno was the preferred solution back then. Actually, there weren't many other ones. And, well, the Opigno team gave Tag1 early access to the development repository, which wasn't public back then, and it looked really good, modern, versus the Drupal 7 version. So that, with the proper entity structure and, well, the Group module as a base, yeah, that made the decision to actually go this way.

[00:10:52] Okay. Now, some problems and smaller rewrites began quite early, because, well, there was some serialized PHP in user dashboards. Opigno notifications were also, well, very custom, and they were replaced by the Message module integration. Discussions were moved to forums. And, well, bugs started to pile up, and the fixes that were put upstream on drupal.org were not merged, not reviewed. So it started quite early.

[00:11:40] And also, in late 2019, AFL-CIO joined the project. Much later, in 2021, the AFL-CIO code base was fully merged. So first it was a separate repository, and then it shared the same code base as the AFT Learning platform. So, yeah, same modules, different config. Some modules were just not enabled on different sites. So, yeah, by 2021 there was a big code base with many, many sites running on it from really different organizations.

[00:12:36] So, no, the patches were not merged, and there were some problems on the way. We tried to contact Opigno, had a meeting with them, but it turned out that nothing is really going to change. Now we know they went the SaaS way, software as a service. So we decided to fork. So the Opigno modules in web/modules/contrib became web/modules/custom; the path changed. Not actually needed, but, well, we didn't have any patches anymore, because we just copied the code from the patched contrib to custom. So that was good. And, well, we weren't vulnerable anymore to releases that introduced new bugs.

[00:13:42] Just for the record, 665 files appeared in the repository. Wasn't there one more file? It would make a better number. We must have forgotten about something. And 100,000-plus lines of code.

[00:14:09] So, yeah, now about the engine. Okay, we wanted to improve navigation, improve the performance of it. There were many redirects, and the navigation logic was a bit buggy. So we created a central service, an engine of the LMS. It happened, yeah, I think in 2022, and it was the start of Drupal LMS, really, because that's object-oriented code that handles progress tracking, navigation logic in courses, initialization of LMS entities, scoring, and contains some maintenance procedures. So from about 4,000 lines of code in the Opigno Learning Path module, and I think 2,000-something in the Opigno Module module, we moved to the training manager, with less than 1,000 lines of code, and object-oriented. It was much easier to work after that happened.

[00:15:28] On the way, yeah, in April of 2024, we released our fork publicly on drupal.org. So it was moved to the contrib folder again. And on the way, Group modernization also happened, from Group 1 to Group 3. I think every developer that worked with Group knows the GroupContent to GroupRelationship change, which was kind of tricky, but luckily Kristiaan wrote an on-site migration.

[00:16:16] Okay. Now, a bit later, the Drupal LMS alpha was released. I think it was around September 2024, and it contained only the clean code, the training manager plus a bit more, and what was missing was supplemented. So it was totally clean of the procedural code and didn't contain any of that spaghetti, only new code, including the training manager. And we started thinking about migrating, how to do it. Actually, we now had two LMS code bases, really two LMS structures, in the same repository at the same time: the new Drupal LMS for testing, and the old one for production sites, which was again moved to custom because, well, since the LMS base is one module, we couldn't have two releases of the same module in one code base with Composer.

[00:17:44] Okay, next, the migration. Here, Mauricio Dinarte was the main engineer and did all the heavy lifting. It was a one-site migration, so it didn't need a second instance to migrate from the old one to the new one. It all just happened on the same site. So that resulted in no downtime. It could be done some time overnight, with less traffic. Students were told not to take courses at that time, but the site was online all the time.

[00:18:38] Okay, now a bit about how Drupal LMS happened. It's also a pattern I noticed in all of my open source projects. I also talked to some people, and it's basically the same. So first, when a project is started, a developer is alone, but then more people come. Eventually there's some shelter. I said it's a long way to go, in general.

[00:19:16] Okay, so the prerequisites. First, not enough paid work, or not enough work even if getting paid. So, time. Exactly. And, well, an engineer has to do something with that time, and going to sleep is not an option. So if one has an unfulfilled need to create, then, yeah, at least in my case, I was just looking for a project. It took some time. I tried doing some things that maybe weren't good ideas, like some accounting framework. But then it just became clear that Drupal has no learning management system that is reliable enough. And that was really bad because, for example, in my home country, Poland, there are 20 universities, more or less, that have sites on Drupal. Drupal is really a lot into education. So no reliable LMS, that just didn't look good. So there was a gap to fill, and the community needed it. The Opigno maintainers, there was a public invitation to fork the project already. So, yeah, basically that's how it happened.

[00:20:56] Now, I had a question yesterday, if I get paid for Drupal LMS development. Well, not really. It doesn't work that way. So what did I get for open source contributions? In the case of Views Bulk Operations, a bottle of good rum. That was very nice. Work at Tag1 Consulting. And in the case of Drupal LMS, well, it's being able to attend those awesome Drupal conferences and meeting the community and my team. There's also give, take, balance, satisfaction, and the opportunity to learn a lot. But in the case of getting paid, or supporting the project: when a paid project has some needs, we just do them upstream on Drupal LMS instead of in a private repository. So it's the same work; it's just a contribution then.

[00:22:19] Okay, so it started with a passion project. What was important was the individual engineering initiative, but also the client validation. That was very important. So we had those platforms, we had a lot of feedback from the clients on what can be improved, we had the previous history of changes and what's actually needed for an e-learning platform. Also, there was a lot of support from Tag1 Consulting: trust, support, reviews. Well, yeah, it allowed me to gain confidence.

[00:23:21] There's also the community. So we have the LMS channel on Slack, and it currently has almost 100 members. The most important people here: Nat Catchpole, he's a bridge to the core APIs. So whenever I try to do something that is too custom, Nat tells me, "No, there's an API in core for that." There's Oberon. He's from Canada. He's more of a front-end developer, and thanks to that guy, Drupal LMS looks good even on the default Drupal theme, and we have single directory components and additional pedagogy plugins, like fill in the gaps with drag and drop. There's also Jeff Mann from AFL-CIO, who gave us huge, huge testing feedback from the clients. So he's the main bridge between the end users and the development team. Mauricio Dinarte, the migration. So now the LMS migration module is also online, and sites can use it.

[00:24:56] We already have feedback from other companies on the Slack channel that migrations to Drupal LMS do happen and they are successful. Also, the 107 team did the first successful migration and, well, told us about it. We have monthly meetings, which is very nice, because we can talk about some progress for Drupal LMS with, well, others that are in the Slack channel. Yeah, thanks to them, we have lots of input on improvements, general modern code, and, yes, a lot of positive feedback.

[00:25:49] And now, okay, we'll skip that. Actually, we have time. Okay. So, the current state of Drupal LMS in the recipes era. Well, it's production ready. We have a stable 1.2 release. It has single directory components, revisioning support, anonymous user tracking, good performance improvements, and, well, there's an ecosystem of modules, for example, for content import and export. There are some plans, but there were plans before, and they get revised over time. So, but yeah: recipes, translation support, Commerce integration, a bit more on notifications. Yeah, we'll see.

[00:27:01] Now a bit of a showcase of what can be done with Drupal core APIs. Those are nice things. Okay. So we had the course manager in Opigno, which was a React app. Here, course editing is done on one page with Drupal entity forms. The new thing here is an API that allows you to nest those entity forms in modal dialogs. The difference here is that the forms get submitted to a special API endpoint, and it is responsible for submitting the form, rebuilding the parent form, saving the form state to cache. Well, basically it uses Drupal core form APIs and the form cache. So, yeah, it's one more API. Those things are actually used in the Paragraphs module, the Entity Browser module, and in core in the Media Library. We have similar use cases but different solutions. It would be good one day to move that to core, so all those things could use the same API. That's a long story.

[00:28:39] Now, we also have entity storage for anonymous users. So we cannot base it on normal database entity storage here. We had to figure out a way to store those entities and the references, and session storage proved to be good here. So it's a temporary storage for learning statuses and answers in the user session. So there can be some demo course, or we can have an entire site with courses that are available without logging in.

[00:29:33] What's also quite interesting is the activity types and activity answer plugin system. So it's also the Drupal core Entity API and entity type API, bundles. Activity types are bundles that contain a reference to the selected plugin. The plugin is responsible for displaying the answer widget. And activity types are fieldable, so we can make them display any way we want. We can add fields. We can add a video and then an answer plugin, which is, for example, true/false or text input under the video, and we can mix it any way we want, and we can even use layouts to display those. Also, answer plugins are configurable, and that configuration can be done on the activity types.

[00:30:42] All right, that's almost all. So, to summarize the main takeaways. We kept the blueprint from Opigno, the data structure, but we rebuilt everything with modern Drupal core APIs, including the Group module, which was with us all the time. By the way, Kristiaan also joined Tag1 about a year and a half ago, so we're all on the same team now. With the dependencies also: yes, the composable model is much more flexible than a distribution. So when choosing a distribution, one should really evaluate it well and see if there are no traps there, because it can be a very long story afterwards. Of course, if there is another choice.

[00:31:59] And, yeah, how to perform such a live switch, a live migration. In our case, what works is isolating the engine, then working on everything else and slowly adding different components. And also, usually in long-running projects, the code quality drops. There is more and more legacy code, which makes development harder, more time-consuming, and, well, less rewarding. So we managed to reverse the trend here and make the project a better place to work. All right, that would be it. Thank you.

[00:32:59] Since we're missing a volunteer, I'll be handing the microphone if you have any questions. I even have a volunteer t-shirt in my backpack, but, yeah, I'll skip this one. Okay, any questions?

[00:33:20] (From the audience: Just checking if this is working. Yes, it's working. Thank you for the presentation. I have just one question about recipes; you told us about them. Is there any release date for recipes, and do you intend to have a site template just for the LMS case? It would be simpler for introducing new...)

[00:33:52] Yeah. There is one demo recipe done by Mark Felton from our team. I think if you search for "LMS demo," you will find it.

[00:34:10] (From the audience: Okay. Thank you.)

[00:34:11] I still have to link it on the main project page.

[00:34:15] (From the audience: Okay. Thank you.)

[00:34:25] All right. Any more questions?

[00:34:36] (From the audience: First of all, I'd like more details about this bottle of rum. What? And a follow-up question: have you interacted more with Opigno since this has launched, and gotten any feedback from them one way or the other, or are they kind of silent with their own product now?)

[00:35:02] No, it didn't happen. I did a bit of investigating on Opigno, and, yeah, they moved to an enterprise software as a service solution. So, yeah, they are not supporting Opigno as an open source project anymore. Well, probably a business decision.

[00:35:26] (From the audience: So where did the rum come from, and what was it?)

[00:35:31] Which rum?

[00:35:33] (From the audience: You mentioned you got a bottle of rum as part of your open source work.)

[00:35:37] Yeah, this is very nice. I helped one guy with a Views Bulk Operations plugin, an action plugin. So, yeah, he wanted to pay me, but it felt strange. I just told him, "You can buy me a bottle of rum." That was very, very nice, opening the package. Open source is cool.

[00:36:10] (From the audience: Yeah, just about the bottle of rum, I'm just wondering if you ever gave the clients that brought LMS a bottle of rum. That's actually not my question, but it is actually a question I was going to ask. My question was the AI piece of the grading. How does that work?)

[00:36:31] Oh, there was...

[00:36:32] (From the audience: Asking for a friend.)

[00:36:33] I think it was there in the... Okay. Yeah. Okay. So there's a prompt. Are you asking about the activity? Yeah. So there is a source of truth where you can input any text, like Markdown; it's a CKEditor. And, well, there is a prompt that tells the AI to evaluate the student's answer based on the source of truth, and some additional instructions, so not to give the answer right away, to just give some hints. And also there's a conversation, so the student can reply multiple times and correct the answer. So there is some default prompt, but the teacher can customize it, and there is the source of truth.

[00:37:29] (From the audience: So would that work with journal entries, or is it just one-word responses, or...?)

[00:37:35] No, it's in a text field.

[00:37:39] (From the audience: Great.)

[00:37:39] So it's a descriptive answer, and yes, it would work with journals.

[00:37:43] (From the audience: I have a feeling your client, the AFT, would really like that.)

[00:37:47] All right, why not? Right. Any more? We still have a bit, four minutes. If not, then let's wrap up. Thank you all for attending. Looks like many people went to parties yesterday.

Event Details

Conference
DrupalCon Europe
Date
September 30, 2026
Location
Rotterdam, Netherlands
Skill Level
Anyone

Work With Tag1

Be in Capable Digital Hands

Gain confidence and clarity with expert guidance that turns complex technical decisions into clear, informed choices—without the uncertainty.