lcms[2024 — now]
Master Courses and Child Templates
A lesson and course authoring platform for a US online school, with 23,000 students and over a thousand teachers. Content designers build the courses, and teachers deliver them. I have designed it from day one, when Euvic took the platform over from a previous vendor. I work remotely with the client in the US, and with a business analyst and the engineers at Euvic. The platform is shipped, and the client has extended the contract.
What I designed
From the start I designed the core of the product: the lesson editor, the course builder and about 40 component types, built so the courses from the old platform could be migrated into them. With one other designer, who has since moved to another team, I built the client's design system from scratch, and I keep it current as Figma moves from styles to variables to slots. This page is about one part of that work, master courses and the sync between them.
Every variant of a course was a separate course
One course, duplicated for each variant. The copies were independent, so they drifted apart, and a correction had to be made in every one of them by hand.
Content designers build a master course once. Each variant is a child template that inherits everything from it, and a child can be set to differ in specific places. Teachers deliver their courses from the child templates.
Editing an element in a child makes it local. The child stays attached, and a sync overwrites what is still inherited and skips what was edited. So after a sync, the interface has to show what it changed and what it left alone.
The model had a cost. Every existing course had to be migrated, and the content designers had to learn a way of working they had not asked for.
The warning would have fired on every course
A missing element in a child was shown as a warning.
A skipped element is marked on its own row, in its own colour, and the sync still reports success.
Children are supposed to differ, so a warning would fire on the normal case. I took the design to a client session before it was built. The client said there would be many of these, and that once the school year started every course would sit in warnings. So a skipped element stopped being a warning.

Three children skipped this element, listed on the right. The synchronisation at the top left is still ticked.
Change history, from a flat list to a tree
A flat list of changes, each row carrying a breadcrumb saying where in the course that element lived.
Then a hierarchy where a parent row carried its own change and status as well as the ones nested under it.
A parent row carries the path and a count. Every change is a line underneath, including the parent’s own.
A breadcrumb is fine on one row. With twenty rows, the reader has to rebuild the same tree from the breadcrumbs for every row, so I turned the list into a tree. My first hierarchy did not work either: the parent row carried its own status, and it was unclear whether that status belonged to the parent or summed up the lines below. The pattern is recent, and nothing else in the platform uses it yet.

Five rows, five repetitions of Unit 1 / Topic 1 / Day 1.

The chip on the Day 1 row reads Modified Settings. A tick sits beside it.

Day 1 now carries (3) and nothing else. Settings has moved down beside the lessons.
Between the client and the backend
This is the typical loop. The client asks for something, I take it to the engineers, and the backend often cannot do it that way, because it has many rigid constraints. I change the design and bring a middle ground back to the client in our regular sessions. The client's people in those sessions are content designers themselves, so each session is also an informal usability test of the prototype. Improvement requests from other content designers and teachers come to me as well. The retry below is a variation on that loop.
Retrying one failed update
A retry re-ran the entire set of updates, because that is what the system supported.
Retry the batch from the top, or open one failure and retry only that update.
I raised this one. A failed batch blocked the next sync even for the children that had already succeeded. The client agreed. The system only allowed a whole-batch retry, so it became a question of what engineering could be persuaded to build. Individual retry shipped. While the decision was open, I designed it both ways: a retry for the whole batch only, and a retry for each update.

19/20, and the one that failed is opened underneath with its own error.
Two things I have not fixed
Component editing happens in a column that turned out too narrow. That one is mine to fix. A sync still locks editing while it runs, so edits cannot corrupt a run in progress. Lifting the lock is a backend decision, and I keep proposing it.
What I am working on now
The next lesson editor. I am prototyping it in code with Claude Code, on an HTML and CSS kit built from the design system. I am building the design half of the team's AI workflow, so what I prototype goes straight into the engineers' AI-driven development. It is much faster than working in Figma.
What I am not claiming
The client is not named. There are no adoption or time-on-task numbers yet.