Skip to content

Commit 9540aa8

Browse files
committed
fix: broaden scope to include when-to-use
1 parent d658926 commit 9540aa8

1 file changed

Lines changed: 25 additions & 19 deletions

File tree

oeps/best-practices/oep-0068-bp-naming-identifiers.rst renamed to oeps/best-practices/oep-0068-bp-resource-identifiers.rst

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
.. _OEP-68 Naming Identifiers:
1+
.. _OEP-68 Resource Identifiers:
22

3-
OEP-68: Naming Identifiers
4-
###########################
3+
OEP-68: Resource Identifiers
4+
##############################
55

66
.. list-table::
77
:widths: 25 75
88

99
* - OEP
10-
- :ref:`OEP-0068 <OEP-68 Naming Identifiers>`
10+
- :ref:`OEP-0068 <OEP-68 Resource Identifiers>`
1111
* - Title
12-
- Naming Identifiers
12+
- Resource Identifiers
1313
* - Last Modified
1414
- 2026-03-18
1515
* - Authors
@@ -36,7 +36,7 @@ OEP-68: Naming Identifiers
3636
Abstract
3737
********
3838

39-
Open edX code uses several distinct types of identifiers. To make code unambiguous and readable, we adopt consistent naming conventions for each type. This OEP defines five categories of identifiers—Primary Keys, OpaqueKeys, Codes, UUIDs, and Other—and specifies the naming conventions for each. These conventions apply across Python code, database column names, REST API fields, event schemas, and any other context where identifiers appear.
39+
Open edX code uses several distinct types of resource identifiers. To make code unambiguous and readable, we adopt consistent naming conventions for each type, and provide guidance on when each type should be used. This OEP defines five categories of identifiers—Primary Keys, OpaqueKeys, Codes, UUIDs, and Other—and specifies the naming conventions for each. These conventions apply across Python code, database column names, REST API fields, event schemas, and any other context where identifiers appear.
4040

4141
Motivation
4242
**********
@@ -78,7 +78,7 @@ Summary
7878
- ``str``
7979
- ``*_key_string`` (or ``*_key`` when unambiguous)
8080
* - Code
81-
- Locally-scoped slug-like string
81+
- Locally-scoped slug-like string component of an OpaqueKey
8282
- ``str``
8383
- ``*_code``
8484
* - UUID
@@ -93,9 +93,11 @@ Summary
9393
Primary Keys
9494
============
9595

96-
Every Django model uses ``django.db.models.BigAutoField`` as its primary key. This is an auto-incremented integer assigned by the database and meaningful only within that database; it should not be used as a stable cross-service or cross-instance identifier.
96+
Every Django model uses ``django.db.models.BigAutoField`` as its primary key. This is an auto-incremented integer assigned by the database and meaningful only within that database; it is not a stable cross-service or cross-instance identifier.
9797

98-
The preferred name for a primary key is ``pk`` (for the key of the current model) or ``<entity>_pk`` for foreign references. The suffix ``_pk`` makes it immediately clear that the value is a database-internal integer, not a globally meaningful identifier.
98+
**When to use**: Primary keys are the default choice for referencing database rows. Prefer them over OpaqueKeys when query efficiency is a concern, and *always* use primary keys for foreign key relationships on Django models. When readability matters more than efficiency—such as in logs, URLs, admin views, reporting, events, REST APIs, and advanced user interfaces—consider using an OpaqueKey instead.
99+
100+
**How to name**: The preferred name for a primary key is ``pk`` (for the key of the current model) or ``<entity>_pk`` for foreign references. The suffix ``_pk`` makes it immediately clear that the value is a database-internal integer, not a globally meaningful identifier.
99101

100102
.. code-block:: python
101103
@@ -114,9 +116,11 @@ The names ``id`` and ``<entity>_id`` are historically common in Open edX code an
114116
OpaqueKeys
115117
==========
116118

117-
An ``OpaqueKey`` (defined in `openedx/opaque-keys`_) is a parsed Python object that globally and stably identifies a resource across an entire Open edX instance. OpaqueKeys are serialized with a key-type prefix followed by a colon (e.g. ``course-v1:``, ``lb:``).
119+
An ``OpaqueKey`` (defined in `openedx/opaque-keys`_) is a parsed Python object that globally identifies a resource across an entire Open edX instance. OpaqueKeys are serialized with a key-type prefix followed by a colon (e.g. ``course-v1:``, ``lb:``).
120+
121+
**When to use**: Use OpaqueKeys when identifier readability matters—for example, in logs, URLs, admin views, reporting, events, REST APIs, and advanced user interfaces. When the same resource has both a primary key and an OpaqueKey, prefer the primary key for database queries and foreign key references (for query efficiency), and prefer the OpaqueKey where human readability is valuable.
118122

119-
Variables and fields holding a parsed ``OpaqueKey`` object should use the suffix ``_key``.
123+
**How to name**: Variables and fields holding a parsed ``OpaqueKey`` object should use the suffix ``_key``.
120124

121125
.. code-block:: python
122126
@@ -165,7 +169,9 @@ Codes
165169

166170
A code is a short, locally-scoped string identifier. Codes follow slug rules: they consist only of alphanumeric characters, hyphens, and underscores (equivalent to Django's ``SlugField``). They are case-sensitive. Unlike OpaqueKeys, codes are not globally unique on their own; they identify a resource or aspect of a resource *within some enclosing context*.
167171

168-
Codes are the building blocks of OpaqueKeys. For example, the serialized ``UsageKey`` ``"lb:Axim:ChemLib:problem:Atoms6"`` is composed of the prefix ``lb:`` and the following codes:
172+
**When to use**: Codes almost always exist as components that are composed into an OpaqueKey. If you find yourself reaching for a code-like identifier that is *not* destined to become part of an OpaqueKey, consider whether one of the other identifier types is more appropriate.
173+
174+
**How to name**: Variables and fields holding a code should use the suffix ``_code``. Codes are the building blocks of OpaqueKeys. For example, the serialized ``UsageKey`` ``"lb:Axim:ChemLib:problem:Atoms6"`` is composed of the prefix ``lb:`` and the following codes:
169175

170176
.. code-block:: python
171177
@@ -174,18 +180,16 @@ Codes are the building blocks of OpaqueKeys. For example, the serialized ``Usage
174180
type_code = "problem"
175181
block_code = "Atoms6"
176182
177-
Variables and fields holding a code should use the suffix ``_code``.
178-
179-
.. code-block:: python
180-
181183
def get_library(org_code: str, library_code: str) -> ContentLibrary: ...
182184
183185
UUIDs
184186
=====
185187

186-
A UUID (Universally Unique Identifier) is a 128-bit identifier that uniquely identifies a resource across *all* Open edX instances, not just within one. UUIDs are suitable for use as stable identifiers in cross-instance contexts such as event data, external integrations, and shared databases.
188+
A UUID (Universally Unique Identifier) is a 128-bit identifier that uniquely identifies a resource across *all* Open edX instances. Unlike primary keys and OpaqueKeys, UUIDs are not scoped to a single database or instance.
187189

188-
The preferred Python type for a UUID is ``uuid.UUID``. Variables and fields holding a UUID should use the suffix ``_uuid``.
190+
**When to use**: Use UUIDs when you need a stable identifier that remains meaningful across Open edX instances—for example, in cross-instance event data, external integrations, and shared databases. If the identifier only needs to be unique within a single instance, an OpaqueKey or primary key is more appropriate.
191+
192+
**How to name**: The preferred Python type for a UUID is ``uuid.UUID``. Variables and fields holding a UUID should use the suffix ``_uuid``.
189193

190194
.. code-block:: python
191195
@@ -231,7 +235,9 @@ Other Identifiers
231235

232236
Not every identifier fits neatly into one of the above categories. When an identifier does not, choose a name that does not use any of the suffixes ``_pk``, ``_key``, ``_key_string``, ``_code``, ``_uuid``, or ``_uuid_string``, so that readers are not misled into assuming a type or scope that does not apply.
233237

234-
For inspiration, consider the ``refname`` field on ``PublishableEntity`` objects in ``openedx-learning``. A ``refname`` correlates a database entity with its representation in off-platform or cross-platform ZIP archives. It is not a primary key (which would be database-specific), not a code (because it may contain non-slug characters), and not an OpaqueKey (because it cannot be parsed into a globally-scoped identifier). By choosing the name ``refname``—which collides with none of the conventions above—the code signals clearly that this identifier is its own distinct thing.
238+
**When to use**: Use this guidance when you have confirmed that an identifier does not fit any of the four named categories above. Before settling on a novel identifier type, consider whether a primary key, OpaqueKey, code, or UUID would serve the purpose.
239+
240+
**How to name**: Choose a name that is evocative of the identifier's specific meaning and that does not collide with any of the conventions above. For inspiration, consider the ``refname`` field on ``PublishableEntity`` objects in ``openedx-learning``. A ``refname`` correlates a database entity with its representation in off-platform or cross-platform ZIP archives. It is not a primary key (which would be database-specific), not a code (because it may contain non-slug characters), and not an OpaqueKey (because it cannot be parsed into a globally-scoped identifier). By choosing the name ``refname``—which collides with none of the conventions above—the code signals clearly that this identifier is its own distinct thing.
235241

236242
Rationale
237243
*********

0 commit comments

Comments
 (0)