Experimentos
Los experimentos son una forma de pruebas A/B que utiliza Discord tanto del lado cliente como del servidor en sus aplicaciones para mostrar diferentes experiencias o comportamientos a usuarios diferentes aleatoriamente y/o basados en la ubicación, versión del cliente, etc.
En esencia, las implementaciones (rollouts) son simplemente YAML introducido en el panel administrativo de Discord; sin embargo, no todos los datos son requeridos por los clientes. Por eso los experimentos que los clientes ven están minificados y resultan complejos de descifrar.
Huella Digital
Incluso el sitio web de marketing utiliza tests A/B para atraer usuarios a la app. Por ello, la aplicación necesita una forma única de identificar a quien usa la web sin usar autenticación (para usuarios que inicialmente visitan la landing page), así que recurre a "fingerprints" o huellas digitales.
Estas no son las típicas fingerprints generadas por información del navegador; son snowflakes generados por peticiones no autenticadas a obtener asignaciones de experimento. Se espera que las fingerprints se envíen en la cabecera X-Fingerprint
en todas las peticiones posteriores a la API hasta autenticarse, para rastrear las pruebas A/B y permitir el acceso a partes de experimentos limitadas por API.
Una fingerprint consiste en un snowflake y un valor criptográfico hasheado. Tiene este aspecto: 1084179945133187083.JQddgNMmwJPghoBtFmaH7jTmdsw
.
Al registrar una nueva cuenta, la fingerprint se pasa, y (si es válida) se utiliza como ID del usuario creado. Esto es para preservar los experimentos en el usuario que acaba de registrarse. Por tanto, el tiempo de creación de una cuenta teóricamente representa la primera vez que visitó la web de marketing de Discord.
Implementaciones
Las implementaciones de experimentos se definen en poblaciones basadas en la posición de implementación del usuario o servidor y pueden tener filtros que limiten la disponibilidad de cada población.
Ejemplo de implementación
2023-02_stage_boosting (1816004721)
### Treatment 1
Filters
Guild Features: [COMMUNITY]
Member Count Range: 1000 - null
Hash Range: Hash Key: 1816004721, Target: 10000
Position Ranges
5000-9500, 9500-10000
### Control
Filters
Guild Features: [COMMUNITY]
Member Count Range: 1000 - null
Position Ranges
0 - 10000
### None
Position Ranges
0 - 10000
Tratamientos
Control
y None
están representados en la API como los valores enteros 0
y -1
, respectivamente. Como advertencia, hay instancias donde ciertos experimentos tienen tratamientos innecesarios etiquetados como Control
indexados como 1
.
Posiciones de implementación
Una posición de implementación se calcula con el siguiente seudocódigo, donde exp_name
es el nombre del experimento y resource_id
es el ID de usuario, fingerprint o servidor. Esta posición se usa junto con las poblaciones y filtros para averiguar a qué bucket ha sido asignado el experimento simplemente comprobando en qué tratamiento entra la población.
result = mmh3.hash('exp_name:resource_id', signed=False) % 10000
Estructuras de datos
Hay dos tipos de experimentos, experimentos de usuario y experimentos de servidor. Los metadatos y nombres legibles por humanos están disponibles en los clientes. En la API los objetos no traen estos datos, salvo los experimentos de servidor, que pueden tener el nombre para el hash.
La mayoría de los objetos siguientes están representados como arrays siguiendo el orden documentado en los campos.
Experimentos de usuario
La información devuelta por la API sobre experimentos de usuario es muy limitada. En contraste con la variedad de datos de los rollouts de servidor, solo obtenemos el bucket asignado al usuario o la huella digital.
Los experimentos de usuario aplican los mismos filtros y funcionamiento que los de servidor, aunque solo se da el resultado calculado.
Estructura de experimento de usuario
Este objeto es un array de los siguientes campos:
1 El bucket para experimentos A/A test debe ser None (-1
) salvo que haya sobrescritura para el recurso.
2 La información de holdout está presente solo si hay bucket asignado. Si hay holdout y el bucket es None (-1
), el experimento está deshabilitado por el holdout. Los clientes pueden omitir este campo y solo seguir el campo population
como siempre.
hash
integer
Hash Murmur3 de 32 bits sin signo del nombre del experimento.
revision
integer
Versión actual del rollout.
bucket
integer
El bucket experimental asignado al usuario o fingerprint.
override
integer
Indica si el usuario o fingerprint tiene sobrescritura para el experimento (-1
falso, 0
verdadero).
population
integer
El grupo interno de población al que pertenece el usuario o fingerprint.
hash_result
integer
La posición de implementación calculada a usar, priorizada a cálculos locales.
aa_mode 1
integer
Modo A/A testing, representado como booleano entero.
trigger_debugging
integer
Indica si trigger debugging de análisis está activado (booleano entero).
holdout_name 2
?string
Nombre legible de un experimento (formato año-mes_nombre
) que deshabilita el experimento.
holdout_revision 2
?integer
Revisión del experimento holdout.
holdout_bucket 2
?integer
Bucket experimental asignado para el experimento holdout.
Ejemplo de experimento de usuario
[826493636, 3, -1, -1, 0, 203, 0, 1, "2025-02_user_profile_editing", 2, 0]
Objeto de población experimental
Define los filtros y rangos de posición que debe cumplir una población.
Estructura de objeto de población experimental
Es un array de los siguientes campos:
ranges
array[objeto rango de población experimental]
Los rangos para esta población.
filters
objeto de filtros de población experimental
Los filtros que el recurso debe cumplir para entrar en la población.
Ejemplo población experimental
[
[
[
-1,
[
{
"s": 7200,
"e": 10000
}
]
]
],
[
[
2294888943,
[
[2690752156, 1405831955],
[1982804121, 10000]
]
]
]
]
Objeto de rango de población experimental
Si los filtros de la población son válidos y el rango incluye la posición de rollout, el recurso es elegible para ese bucket.
Estructura de objeto de rango de población experimental
Este objeto se representa como un array de los siguientes campos:
bucket
integer
El bucket que otorga este rango.
rollout
array[objeto rollout de población experimental]
El rango del rollout.
Estructura rollout de población experimental
s
integer
El inicio de este rango.
e
integer
El final de este rango.
Ejemplo de rango de población experimental
[
1,
[
{
"s": 0,
"e": 4750
}
]
]
Objeto de filtros de población experimental
Define los filtros necesarios para ser elegible para los rangos. Todos los filtros deben cumplirse para que el recurso sea elegible al bucket.
Los filtros son un objeto representado como array de arrays. El primer elemento del array anidado es el key hash, el segundo el valor. El orden es siempre el documentado aquí.
Estructura filtros poblacionales experimentales
guild_id_range?
objeto filtro rango por ID
Rango de IDs de recurso elegibles.
guild_age_range_days? 1
objeto filtro rango por días
Rango de días de vida del servidor elegibles.
guild_member_count_range?
objeto filtro rango por miembros
Rango de cantidad de miembros elegibles.
guild_ids?
objeto filtro por ID
Lista de IDs de recurso elegibles.
guild_hub_types?
objeto filtro tipos de hub
Lista de tipos de hub elegibles.
guild_has_vanity_url?
objeto filtro vanity url
Si el servidor debe tener vanity url para ser elegible.
guild_in_range_by_hash?
objeto filtro por hash/rango
Límites especiales de posición de implementarción.
1 La edad del servidor se determina por el ID. Consulta la documentación de snowflake. Puede calcularse así:
timestamp = ((resource_id >> 22) + 1420070400000) / 1000
guild_age = (time.time() - timestamp) / 86400
Estructuras de filtros
guild_features
array [string]
Las funcionalidades del servidor elegibles; con que tenga una basta.
Estructura filtro rango por hash
hash_key
integer
Hash Murmur3 de 32 bits sin signo usado para determinar elegibilidad.
target
integer
Límite de posición para esta población.
Ejemplo filtros de población experimental
[
[
1604612045, // guild_has_feature
[
[
1183251248, // guild_features
["ROLE_SUBSCRIPTIONS_ENABLED"]
]
]
]
]
Objeto de sobrescritura de bucket experimental
Una sobrescritura representa el ajuste manual por empleados de Discord para dar acceso temprano o concreto a un experimento.
Estructura de sobrescritura de bucket experimental
b
integer
Bucket asignado a estos recursos.
k
array [snowflake]
Recursos que se les concede acceso a este bucket.
Ejemplo de Sobrescritura de Bucket
{
"b": 1,
"k": ["882680660588904448", "882703776794959873", "859533785225494528", "859533828754505741"]
}
Endpoints
Obtener asignaciones de experimento
GET /experiments
Devuelve las asignaciones de experimento de usuario y opcionalmente los rollouts de servidor para el usuario o fingerprint.
Una fingerprint solo se genera si no hay autorización ni fingerprint en las cabeceras de petición. El límite es 3 fingerprints válidas por 2 minutos por IP. Las extra no serán válidas.
Parámetros de cadena de consulta
1 Incluir este parámetro requiere autenticación válida.
with_guild_experiments?
boolean
Si devolver experimentos de servidor en la respuesta.
platform? 1
string
Si incluir experimentos para la plataforma dada.
Plataforma de experimentos
DEVELOPER_PORTAL
El portal de desarrolladores.
Cuerpo de respuesta
assignments
Asignaciones de experimento para ese usuario o fingerprint.
guild_experiments?
Rollouts experimentales de servidor para el cliente asignar.
Crear fingerprint
POST /auth/fingerprint
Genera una nueva fingerprint.
El límite es de 3 fingerprints válidas por 2 minutos por IP. Más allá de este límite, se devuelven pero no serán válidas.
Cuerpo de respuesta
fingerprint
string
La fingerprint generada de la fecha y hora actual.