Migrating Universal Kids From Drupal 7 to Drupal 8 Without Rebuilding the React Front End

Consolidating a Decoupled Architecture for an NBCUniversal Property

How a four-person Lullabot team migrated NBCUniversal's Universal Kids site from Drupal 7 to Drupal 8, retiring a legacy Java/MongoDB layer while preserving the React single-page application's API contract.

Executive Summary

Universal Kids is a cable television brand owned by NBCUniversal. Its web presence ran on an architecture that had grown more complicated than it needed to be: a Drupal 7 content management system paired with a legacy Java application and a MongoDB database, all feeding a decoupled React single-page application (SPA) on the front end.

That architecture wasn’t unreasonable when it was built — decoupling a front end from a CMS backend was, and still is, a legitimate pattern. But three separate systems (Drupal 7, the Java application, and MongoDB) sharing responsibility for content and data meant three separate things that could break, three separate things to patch and keep secure, and three separate mental models for anyone trying to reason about where a given piece of content actually lived.

Lullabot was brought in to migrate the CMS from Drupal 7 to Drupal 8 and, in the process, consolidate that three-system backend into a single Drupal 8 installation — while leaving the React front end that editors and viewers already depended on intact, and without breaking the API contract that front end relied on.

I worked on this project as a Senior Developer, alongside a small, named team: Andrew Berry (VP of Technology), Juampy NR (Lead Engineer), and Salvador Molina Moreno (Senior Developer). Because the public record names only four people on this engagement, I can speak somewhat more directly about the kind of work a senior developer on a team this size would be doing — while still being clear that specific architectural calls were made collaboratively, led by the project’s Lead Engineer, not unilaterally by any one contributor.


The Environment Before Migration

Before the migration, Universal Kids’ stack looked like this:

LayerRole before migration
Drupal 7Primary CMS for editorial content
Legacy Java applicationAdditional application logic, separate from Drupal
MongoDBData storage tied to the Java application
React SPADecoupled front end, consuming APIs from the backend

That arrangement meant the “backend,” from the React application’s point of view, wasn’t really one system. It was two systems — Drupal 7 and the Java/MongoDB application — each exposing some portion of the data the front end needed, independently versioned, independently deployed, and independently at risk of drifting out of sync with each other.

Drupal 7 itself was also aging. Drupal 7’s long-term support timeline, and the broader ecosystem’s shift toward Drupal 8’s object-oriented architecture and Symfony components, made staying on Drupal 7 an increasingly expensive choice regardless of the Java/MongoDB question. Migrating to Drupal 8 wasn’t optional long-term; the only real question was whether to carry the legacy Java/MongoDB layer forward into the new architecture or use the migration as the opportunity to retire it.


Architecture Overview

The target architecture consolidated content and data responsibilities into a single Drupal 8 system, while keeping the React front end’s integration surface intact.

LayerRole after migration
Drupal 8Single system of record for content and data, replacing Drupal 7 + Java + MongoDB
React SPAUnchanged front end, continuing to consume APIs — now served entirely from Drupal 8
VarnishCaching layer in front of the application
Docker / CircleCI / TugboatContainerized builds, CI automation, and disposable preview environments for the migration work itself

The React application didn’t need to know, or care, that its data now came from one consolidated Drupal 8 system instead of two separate systems. That was the point: the migration was designed so the front end’s contract stayed stable while everything behind it changed.

Engineering Approach Note

AN-001 — Consolidate the Backend Without Rebuilding the Front End

Approach
Reconstructed from the stated project scope (migrate CMS, preserve React functionality)
Tension
Backend simplification vs. front-end stability

Context

The React SPA was already working and already depended on by editors and viewers. Rebuilding it alongside the backend migration would have multiplied risk and scope at the same time, for no real benefit — the front end wasn't the part of the system that was causing operational pain.

The Approach

Treat the migration as a backend consolidation project with a hard constraint: the React application's API contract must keep working throughout and after the migration. Drupal 8 takes over the responsibilities previously split across Drupal 7 and the Java/MongoDB layer, but it does so behind the same interface the front end already expects.

Tradeoffs

Preserving an existing API contract while changing everything behind it is more constraining than a clean-slate rebuild would be — every piece of consolidated functionality has to be mapped back to what the front end already expects, rather than designed from scratch. That constraint is also exactly what kept the project's risk bounded.


Preserving the React API Contract

The decoupled front end being migration-agnostic was a deliberate design goal, not an accident. In practice, this meant the team had to understand the full surface area of what the React application was actually consuming from the backend — every endpoint, every data shape — before changing what sat behind it.

Engineering Approach Note

AN-002 — Migrate the Backend Without Breaking the Contract the Front End Depends On

Approach
Reconstructed from the stated outcome ("reduced React app errors")
Risk
Silent contract drift between backend and SPA

Context

A decoupled front end only stays stable if its contract with the backend stays stable. Consolidating two backend systems into one creates many opportunities to subtly change a response shape, an endpoint path, or a data type in ways the front end wasn't written to tolerate.

The Approach

Map the React application's existing API consumption thoroughly, and build the consolidated Drupal 8 backend to serve equivalent data through equivalent contracts, verified against the running front end rather than assumed from documentation alone.

Tradeoffs

This approach trades migration speed for front-end stability. According to Lullabot's case study, the result was a reduction in React application errors post-migration — consistent with a disciplined, contract-first migration rather than a looser "migrate and fix forward" approach.


Retiring Java and MongoDB

Carrying a legacy Java application and MongoDB forward indefinitely alongside Drupal would have meant NBCUniversal continuing to maintain, patch, and operate two independent technology stacks for one property, long after the original reasons for that split had stopped mattering. The migration used the Drupal 8 rebuild as the natural opportunity to fold that functionality into Drupal and retire the legacy layer entirely.

Engineering Approach Note

AN-003 — Retire the Legacy Java/MongoDB Layer Into Drupal 8

Approach
Reconstructed from the stated project scope (consolidate into a single Drupal 8 site)
Why now
The D7-to-D8 migration was already forcing a rebuild of the content layer

Context

Maintaining a separate Java application and MongoDB database alongside Drupal meant two extra systems to patch, scale, and reason about, for functionality that didn't obviously need to live outside Drupal.

The Approach

Fold the functionality the Java/MongoDB layer had been providing into Drupal 8 itself, using Drupal 8's broader content modeling and API capabilities (relative to Drupal 7) to absorb responsibilities that had previously required a separate application.

Tradeoffs

This added scope to the migration — it was no longer a like-for-like CMS upgrade, but a consolidation project. The payoff was a single system of record going forward, with one deployment pipeline and one technology stack to operate instead of three.


A Test-Driven, Disposable-Environment Toolchain

The project’s public listing names a specific toolchain: Docker, MySQL, CircleCI, Tugboat, Varnish, and Git, alongside project-management tooling (Jira, Smartsheet, and a “Throughput Forecaster”). That combination describes a fairly standard, solid modern Drupal delivery pipeline for its time — containerized local and CI environments, automated builds, and disposable preview environments per change, rather than a shared, hand-maintained staging server.

Engineering Approach Note

AN-004 — Disposable Environments and Test-Driven Migration Work

Approach
Reconstructed from the listed toolchain and stated outcome ("TDD uncovered bugs in the existing application")
Tools named in the source
Docker, CircleCI, Tugboat, Varnish, MySQL, Git

Context

Migrating a content layer that three separate systems used to share, while keeping a live front end working, is hard to verify by manual inspection alone. The team needed a way to test migration logic and review changes without risking the production environment or waiting on a single shared staging server.

The Approach

Use Docker-based environments with CircleCI for automated builds and Tugboat for disposable, per-change preview environments, and apply test-driven development practices to the migration logic itself. Per Lullabot's case study, this combination surfaced bugs that existed in the original application — not just migration defects — because writing tests against the existing behavior exposed assumptions that had never been verified.

Tradeoffs

Building out this tooling and writing tests for legacy behavior takes upfront time that a faster, less rigorous migration wouldn't spend. The case study credits this approach with the project launching faster than originally planned overall — consistent with catching integration problems early, in CI and preview environments, rather than late, in production.


Outcomes

Per Lullabot’s public case study, the migration delivered improved site performance, reduced React application errors, a better editorial experience for the content team, and a faster-than-planned launch — with test-driven development surfacing bugs in the pre-existing application along the way.

The case study also includes a client quote:

“Lullabot was an amazing partner…we immediately noticed great improvements to the site’s speed and responsiveness.”

— Samantha Seid, Associate Content Producer, NBCUniversal

That kind of outcome — a non-technical stakeholder noticing speed and responsiveness improvements without being told to look for them — is usually a reasonable signal that a migration project met its real goal: make the system better for the people using it day to day, not just cleaner on an architecture diagram.


Working as a Four-Person Team

This was a small, named team: Andrew Berry as VP of Technology, Juampy NR as Lead Engineer, Salvador Molina Moreno as Senior Developer, and myself as Senior Developer. With only four people credited publicly, it’s fair to say the engineering decisions throughout this migration were made collaboratively among a tight group, under the Lead Engineer’s technical direction, rather than by a large committee or a single architect working in isolation.

As a Senior Developer on that team, my contribution sat within that collaborative structure — working through migration logic, content modeling, and integration details alongside Juampy and Salvador, under Andrew’s technical leadership. The public case study doesn’t attribute specific lines of work to specific individuals, and I’m not going to invent that attribution here. What I can say honestly is that a team of this size doesn’t have room for passengers — everyone on a four-person engineering team for a project like this is doing real technical work, even when the public record credits the team rather than breaking out who did what.


Lessons Learned

A few things generalize beyond this specific project.

Decoupled architectures should decouple on purpose, not by accretion. Universal Kids’ pre-migration stack — Drupal 7, a separate Java app, and MongoDB — wasn’t decoupled as a deliberate design choice so much as it had accumulated that shape over time. Consolidating it back into one system wasn’t a rejection of decoupling as a pattern; it was recognizing that this particular three-way split had stopped earning its complexity.

A stable front-end contract is a powerful constraint for a backend migration. Committing to not touching the React application’s API contract during the migration forced discipline on the backend work, and gave the team a clear, testable definition of “done” that didn’t depend on subjective judgment calls about the front end.

Test-driven migration work finds more than migration bugs. Writing tests against existing behavior, as part of a migration, tends to surface bugs that were already there — which is valuable information, even when it wasn’t the project’s primary goal.

Small teams can take on architecturally ambitious work. A four-person team consolidating three backend systems into one, on a decoupled architecture serving a national media brand, is a meaningful scope for that team size. It worked because the scope was well-bounded: migrate the backend, preserve the front end, retire the legacy layer — not an open-ended rearchitecture.


Approach Notes Summary

NoteTopicNature
AN-001Consolidate the backend without rebuilding the front endReconstructed approach
AN-002Preserve the React API contract through migrationReconstructed approach
AN-003Retire the legacy Java/MongoDB layer into Drupal 8Reconstructed approach
AN-004Disposable environments and test-driven migration workReconstructed approach

These notes describe the shape of the engineering problem and the approach a senior developer on this team would reasonably have taken — not a documented internal decision log. The outcomes they’re grounded in (reduced React errors, improved performance, a faster launch, and bugs surfaced through TDD) are drawn directly from Lullabot’s public case study.


Final Thoughts

Migrations like this one rarely get remembered for the new features they ship — mostly because the goal isn’t new features, it’s making an existing system less fragile without the people who depend on it noticing any disruption. Universal Kids’ editors kept editing, its viewers kept browsing a React application that never had to be rebuilt, and three systems of record became one. That’s a quieter kind of engineering win than launching something new, but for a four-person team taking on a consolidation of this scope for an NBCUniversal property, it was the right kind of win to aim for.

Explore all Engineering Case Studies or read ECS-003: Modernizing IndieCommerce for Independent Bookstores.


© 2024. All rights reserved.