Strategies for Adapt framework IDs

Adapt content is presented in a series of nested containers: a page holds one or more articles; an article holds one or more blocks; and a block holds one or more components. New course builders have been taught "to learn your ABCs." It is a mnemonic for the article-block-component structure that was hard-coded into the framework. It was reinforced by element IDs used in the Adapt demo courses: a-05, b-05, c-05. And it was a good admonition because following the example of working models is a great way to learn.

But sometimes models are mistaken for the principles they embody.  There's no rule that IDs have to begin with "co" or "a" or "b" or "c." That was made very clear when the Adapt authoring tool came on the scene. On import it chewed up our polite little army of IDs in their regiments of "co", "a", "b", and "c." It handed us back a mob of wild-looking UUIDs, each screaming of independence, each looking confusingly like its compatriot, and each ignorant of its place in a hierarchy.

For the rest of this article, let's face the fact that Adapt doesn't need our orderly regiments of IDs. Let's challenge some of our assumptions and see if we can make our lives a little easier with a bit of creativity.

Adapt's requirements for IDs

What is required?

  • Each must be unique to the course.

What is not required?

  • IDs don't have to include letters.
  • They don't have to include numbers.
  • They don't have to reflect the type of object each represents.

Adapt is really flexible when it comes to IDs. You'll need to balance that flexibility with common sense. Can you have an ID with a space between characters? Yes, you can; but I don't advise it. Adapt will function with such an ID, but the ID gets used in other ways, such as to generate class names that appear in template HTML. So the space in that context turns your clever ID into two classes. I bet you could compensate with necessary changes to CSS; but why go through all that. Be creative to make your life easier, not more difficult.

An earthquake at v5.5.0?

With the release of framework version 5.5.0 (2020-05-18), Adapt no longer requires that content be housed in files named course.json, contentObjects.json, articles.json, blocks.json, and components.json. And it doesn't even require that you maintain the traditional MPABC hierarchy. That's a whole lot of flexibility worth exploring! Begin your technical research with GitHub pull request #2711.

The traditional approach to ID generation

The course demo on the Adapt website, which is bundled with the Adapt framework, establishes a pattern for IDs that encodes some metadata.

  • The first characters denote the type of container element it is.
  • A dash separates these letters from digits that follow them.
  • Trailing digits (in association with the preceding letters) ensure IDs are unique.

Let's talk more about those trailing numbers. Traditionally they are a two- or three-digit combination that is incremented by five. So one might see a sequence of block IDs looking like this:
blocks: b-05, b-10, b-15, b-20

The reason for incrementing by five is to facilitate production—for those times when additional content needs to be inserted. New block content slips in easily as b-12 and b-13. And the result is that the IDs of only a few components need to be reordered. Nifty, eh?

It is until you have a situation where a new page is added with two articles and nine blocks. Now you've got some juggling to do. And here's where the first flaw becomes clear: you're reshuffling IDs in order to maintain a sequence that Adapt doesn't need. Adapt doesn't present units of content such as blocks based on the ID. The order is determined by the block's order in the JSON file—or more precisely by its position within the JSON object. The JSON object determines DOM order.

So who then who needs these numbers to be in sequence? Humans do. Especially those team members who don't know that Adapt doesn't care. Have you, like me, spent time updating the IDs on storyboards and scripts in order to keep the team in sync? Or had a well-intentioned team member reorder the IDs in documentation because he noticed the interval of five was not maintained? Humans readily perceive patterns, but the reason for the pattern may not be as apparent.

Flip-flopping components

While the DOM order of components is determined by their positions within the JSON object, you can easily manipulate their visual order in the block. The Vanilla theme includes a CSS class named "reverse-desktop-order". Note the clue in the name: "desktop". This class includes a media query that exclude phones. That makes sense since components viewed on small device widths will stack vertically, not align horizontally side-by-side.

This class reverses the direction of the CSS flex-row containing the components. It is particularly useful for series of blocks that contain Text components paired with Graphic. First arrange your components in the JSON file so that they stack in phone view to your satisfaction. Then view the course on desktop. Remedy visual redundancy by applying "reverse-desktop-order" to any block in order to flip its components.

Breaking from tradition

It doesn't take long to feel the limits imposed by the formula of two digits incremented by five. It can only accommodate some 18 or 19 components before the interval is compromised—or before you add a third digit. But if you can add a third, why not a fourth? or fifth?

Here's the next flaw: the temptation to pack even more metadata into the IDs. Let's face it, the letters at the start of these traditional IDs don't do much for us. They tell us to search for "a"-type code objects in articles.json and "b"-type objects in blocks.json. But what if we could get the numbers to show us the relationship between the article and the blocks it contains? We can build a block ID that uses two digits to reference the article ID and another two digits to sequence the blocks. By doing this, we can tell which blocks belong to which article. More importantly, we've expanded the total number of blocks that the ID strategy can accommodate. With this change, the two-digit sequence dedicated to blocks is restarted with every article. For article a-15, we have blocks b-1505 and b-1510. And for article a-20, we have b-2005 and b-2010. Each article now accommodates 18 or 19 blocks.

OK, since we were able to associate articles and blocks, can we bring in components? Something like c-150505 and c-150510? Of course you can! But hold on a minute. Ask yourself "why?" It's really tempting to have every part of these IDs tell us something. But does this information actually help us be more effective or more productive? Can we build a course faster or more accurately? My answer changes with the course, the e-learning team, the workflow, client documentation—with so many different factors. Guess I can't escape saying it: it depends. I've stopped trusting that the traditional approach to IDs is best for every situation. I've stopped searching for the one that is. I've stopped believing that what makes me more productive has to make you more productive.

Identify what slows you down—and what slows down the team.
Few are the courses that I work on where I am handed a finished storyboard with every block and component plotted, mapped, and ID'd. On all the other courses I build, my efficiency is impeded if I cannot add straight away to every page a terminal block with some secondary navigation like adapt-pageNav. I need an ID strategy that permits that with little fuss.

Identify how the team communicates about units of content.
If the team communicates visually through detailed mock-ups in products such as Miro, Photoshop, or XD, IDs may never be a shared concern. In that case I'd do what makes sense for my own work. However, I'd operate differently if edits and amends are communicated in text through some ticketing system such as JIRA, Trac, or even with a spreadsheet. I'd want to be using a strategy for IDs that accommodates my work AND makes sense to content reviewers.

I'm a developer who's built a couple hundred Adapt courses. Here's some of my needs when working in the framework.
I want an ID strategy that

  • allows me to add a terminal block to the page before anything else is added.
  • reflects the relationship between components and their parent block.
  • handles many units of content.

Here's how I meet those needs when working in the framework:

  • I begin by adding my terminal block to the last article of each page. I mark it as a terminal block by using an ID that ends in "99". I add the Page Nav component. All other content blocks will be added above it in the JSON file. Sometimes I assign the block an empty class such as "terminal" that I later fill with CSS styles for that last block on the page.
  • Since there are at most two components per block, I don't use a two-digit sequence for components. I reuse the bulk of the block ID as the component ID, and I append to it either "-1" or "-2". This preserves the relationship between blocks and components, and it gives me insight whenever I'm styling one or the other.
    block: b-1505
    left component: c-1505-1
    right component: c-1505-2
  • I typically use four digits in a block ID. The first two reflect the parent article ID, and the second two is for the traditional sequence incremented by 5. Because the sequence is restarted with every article, I have never needed more than 19 blocks for an article. However, I wouldn't hesitate to decrease the interval if I were anticipating a tight fit. Nor would I hesitate to decrease the interval if I needed more articles.
    article: a-15; blocks: b-1505, b-1510, b-1515
    article: a-20; blocks: b-2005, b-2010, b-2015
  • I rarely give a thought to the course ID. On occasion I have built out courses with several modules. All the modules/course used the same theme and same framework code base. This is easy to do with rub-cli. Rub extends the functionality of the Grunt tasks in Adapt. It allows you to build multiple courses within the same framework project. In such situations, I use a course ID that reflects the module—typically some identifying code from the team's project management process. Or I make something up. I feel it helps to prevent me from confusing one course.json with another.
  • Pages tend to be a small group of elements, but they come with a twist or two. One twist I've encountered: they are prone to shuffling during development. I avoid that human compulsion to re-sequence numbers by using two or three letter characters to reflect the page's title. If the pages get reordered, nothing feels out of order.
    pages: hr, md, bk
  • In Adapt-speak, pages and menus are types of content objects. Which role they play is determined not by the ID but by the "_type" attribute. I indicate the relationship between a submenu and its pages by appending another identifier for each page.
    contentObjects:
    page: hr
    menu: md
    page: md-dw
    page: md-ot
    page: md-aa
    page: bk

Will this work for you? I suspect that it will in some cases if you have needs that are similar to mine. But the goal of this article is to encourage you to find creative approaches that work for you and your team and for the specific situation.

Alternative strategies

Here's some other patterns, conventions, and approaches to inspire your creativity.

Compound IDs

Abandon the traditional prefix which indicates what kind of element the object is. No more "co-", "a-", "b-", or "c-". Simply repeat the parent ID and append the child ID. Let a dash separate each element. Increment to ensure uniqueness.
page: 05
articles: 05-05, 05-10, 05-15
blocks: 05-05-05, 05-05-10, 05-05-15
components: 05-05-05-05, 05-05-05-10, 05-05-05-15

It's no-frills and easy to comprehend. If your content is compiled in a spreadsheet application, you can easily generate this kind of ID with a formula.

The number of chunks falls well within George Miller's magic seven. But I'd find a way to break off the pages to further reduce the number.  (I just find numbers a bit tiring.)

Janooba's concise IDs

The IDs developed by Kolton Meier (aka Janooba) and his team capture the parent-child relationships of Adapt structural elements, but they do it in a very concise way. Their model:
content object: [#]
article: [A-Z]
separator: _ (underscore)
block: [#]
component: [a|b]

Examples:
content objects: 01, 02
articles: 01A, 01B, 01C, 02A, 02B, 03A
blocks:  01A_01, 01A_02, 01A_03, 01B_01, 01B_02, 02A_01
components: 01A_01a, 01A_01b, 01A_02a, 01A_02b, 01A_03a, 01A_03b

I like how this approach packs a lot of information into a few characters. I like how it can scale to courses with a lot of content units. I even like the underscore that separates the pages and articles (the less referenced elements) from the blocks and components (where the bulk of configuration and styling happens). I don't like so much its reliance on sequences both alpha and numeric, but some of that is unavoidable when brevity is a goal. I think many Adapt developers would find this approach attractive.

Human-readable IDs

Replace the frenzy of numbers with nature. Or at least with a nod  to natural language. I discovered AJ ONeal's efforts with human-readable-ids-js when I did a search for "human readable IDs"—go figure! His strategy is to form IDs from a combination of adjective-noun-number. His examples modified for Adapt:
articles: silly, quick, tricky
blocks: silly-goose, silly-cobra,  silly-chicken, quick-goose, quick-cobra, tricky-goose
components: silly-goose-05, silly-goose-10, silly-cobra-05, silly-cobra-10

I find it sooo much easier to process and remember this kind of ID than a digit-based ID. I don't know if I save time, but I feel as if I expend less psychic energy doing the work. Yep, you've got some prep work if you go this route. But AJ's adjectives and nouns are ready and waiting.

As mentioned earlier, Adapt uses IDs in routing URLs and in HTML templates. So "silly-goose-05" will be integrated in your course code. Most LMSs don't display URLs when they are launched; does yours? And the IDs will be visible within the HTML code—but your learning design has serious problems if learners are more interested in inspecting your HTML! Nonetheless, you'll want to weigh how this type of ID fits with your branding should they be exposed. Frankly, my mind experiences benefits even if the adjectives and nouns are slightly encoded:
sly-gse-05, qik-gse-05, tri-gse-05
My mind simply needs a symbolic prompt; it fills in the blanks. And these shortened versions look a bit more professional.

The future of ABCs

The Adapt framework has already eliminated its reliance on a specific page-article-block-component hierarchy. So there are reasons for Adapt developers to step beyond this specific model. But ABC—or MPABC—isn't disappearing anytime soon. Some classic extensions are still dependent on it. Current versions of Assessment, Page Level Progress, and Branching, for example, won't work without it. Likewise, the Adapt authoring tool still uses it as its model for course construction. And a similar hierarchy of containers is fundamental to HTML.

So as ABC becomes one option among others, developers have another reason to rethink the place of A B C in their IDs.