Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
.. _openedx-tagging-adr-0010:

10. Mutability and Identity of Taxonomy Tags
==============================================

Status
------

Accepted

Context
-------

Taxonomy tags rely on three different identifiers, each serving a distinct purpose:

#. ``id``: the internal, environment-specific database primary key.
#. ``value``: a string used for display, search indexing, and short-lived API interactions. It is unique within a taxonomy, and mutable.
#. ``external_id``: a string used to track a tag's identity across system boundaries, particularly during long-lived use cases like taxonomy import and export.

The system treats ``external_id`` as immutable. The architectural tension arises when an ``external_id`` legitimately needs to change - for example, to correct a typo, or to align with an upstream terminology change (e.g. updating an external standard from "equity considerations" to "use considerations").

If ``external_id`` is strictly immutable, a user must delete the existing tag and create a new one, destroying all existing object associations (foreign keys) tied to that tag.

Conversely, if ``external_id`` were freely mutable, we would break the mechanism used to maintain continuity during taxonomy import/export. Since internal database ``id`` values cannot be used across different environments (they are auto-incremented and environment-specific), the system would have no reliable way to map an incoming, updated tag to the existing tag already in the database.

@bradenmacdonald bradenmacdonald Jul 7, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only true if the external systems also treat the IDs as immutable. But we learned today that some use cases do require IDs to be changed. So we need to support that.


The core problem: how do we uniquely identify tags across decoupled environments while allowing administrators to mutate both display values and external identifiers, without destroying existing data relationships?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, that's by using the external_id field. If administrators want perfect consistency across environments and time, they must not change external_ids. If they do change the IDs within their taxonomy, then they should also publish a changelog that provides the full history to allow that tracking and mapping over time. It's not our responsibility to provide that.

Well-behaved taxonomies should never change their IDs, and if you look at the lightcast skills taxonomy changelog for example, you'll see that that's the case. (Although sometimes tags are merged together, consolidating two IDs into one.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, but whose responsibility this kind of thing is I cannot answer. I assume it's a product question @jmakowski1123

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well-behaved taxonomies should never change their IDs

I guess my question then - related to the other ADR - is: why would it then be okay for us to change the external_id on our side? Wouldn't we then need to be the ones who need to provide tracking and mapping?

@bradenmacdonald bradenmacdonald Jul 7, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because some taxonomies are going to be changing their IDs upstream, so we have no choice but to allow them to be changed on our side too.

Whoever defines the taxonomy at each institution is responsible for publishing the new versions of the taxonomy and the changelog, with all that information. We're not responsible for that; we just need a way to keep the Open edX system in sync.

@jesperhodge jesperhodge Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me.
If I understand your comment on the other ticket correctly, you also think that having some previous_external_id field and a rename that triggers on import make sense, right? To properly support mutability. In that case maybe I should add that mechanism to this PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or did you mean to say we can have mutability and don't need the previous_external_id field or rename?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly speaking, all we need is the ability for admins to edit the external_id in the django admin, but as a nice-to-have we should implement a previous_external_id field on imports in the future. So I think we want both eventually.

In the long term if this turns out to be a common requirement, we could also allow editing it in the UI directly, but I'd probably recommend not implementing that for now, until we know how often it's required.


Decision
--------

Role and scope of identifiers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To maintain clean boundaries between internal systems and external data mobility, the roles of our identifiers are strictly defined:

* **Internal database ``id``:** reserved for internal, environment-specific relational mapping. It is not exposed in import/export payloads, since it has no cross-environment portability.
* **``value``:** the mutable, human-readable label. It is unique within a taxonomy, but is not relied upon as a stable identifier for import/export.
* **``external_id``:** the primary identifier used to match a ``Tag`` across a taxonomy's own import/export. Its scope is limited to that: it is not the identifier used when content tags (``ObjectTag``) reference or export their taxonomy and tag. Content tags instead cache the tag's ``value`` and the taxonomy's ``export_id`` (see :ref:`openedx-tagging-adr-0006`), which is a separate, taxonomy-level identifier.

Handling identifier mutability
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

* The taxonomy editor UI displays external IDs, but doesn't allow changing them. It may allow specifying one when creating a tag, and/or generate one based on the value if none is specified.
* In the rare case where an external ID needs to be changed, an administrator can do so via the Django admin UI.

This matches the current implementation: the tag update REST API only accepts an updated ``value`` (``Taxonomy.update_tag()`` only supports updating a tag's value), while ``external_id`` can only be set at tag creation, or changed directly through the Django admin.

Changelog
---------

2026-07-07:

* Finalized the decision based on the PR discussion, replacing the previously open options for handling identifier mutability.
* Moved from ``docs/decisions/`` to ``docs/openedx_tagging/decisions/``, and renumbered, to match current ADR location and numbering conventions.
* Clarified that ``external_id``'s scope is limited to a taxonomy's own import/export, distinct from ``Taxonomy.export_id``, which content tags use to reference their taxonomy (see :ref:`openedx-tagging-adr-0006`).