Cómo enviar notificaciones a Slack desde PHP, macOS y Windows

En la actualidad trabajamos con máquinas que realizan procesos largos
o, incluso, esas máquinas están lejos de nosotros. Cuando sucede algún
evento que nos interese, como que ha terminado de procesar un fichero
grande o nuestro sitio web está recibiendo un ataque, recibir una
notificación de este hecho podría ayudarnos a ser más proactivos y vivir
más tranquilos.

Construir un sistema de notificaciones puede ser una tarea enorme e,
incluso, cara económicamente. Pero existen alternativas ya
construidas.

Una forma sencilla de construir nuestro propio sistema de
notificaciones consiste en utilizar Slack y su posibilidad de crear aplicaciones o bots.

La ventaja es que nuestros programas no necesitan conocer cómo
funciona el sistema de notificaciones de Windows, MacOS, Android o iOS.
Solamente tienen que enviar un mensaje a Slack.

¿Qué es Slack?

Slack es una plataforma de comunicación basada principalmente en mensajes organizados mediante espacios de trabajo, canales y conversaciones.

Slack está principalmente orientada a equipos de trabajo pero también
puede resultar útil como herramienta personal. Slack dispone de
aplicaciones para diferentes sistemas operativos y dispositivos móviles,
por lo que un mensaje enviado a nuestro espacio de trabajo puede
convertirse inmediatamente en una notificación en nuestro ordenador,
teléfono móvil o smartWatch, dependiendo de cómo tengamos configuradas
las notificaciones.

Esta característica permite utilizar Slack como intermediario entre
nuestros programas y nuestros dispositivos.

Aprender creando

La mejor forma de aprender algo es haciendo algo práctico. Vamos a
crear la infraestructura necesaria para enviar desde nuestro PC con
Windows, nuestro Macbook con MacOS o nuestro hosting web con PHP un
mensaje a Slack para que nos llegue a todos nuestros dispositivos
conectados a Slack.

Técnicamente haremos esto: nuestro programa detectará un determinado
acontecimiento y realizará una petición HTTPS a la API de Slack. Slack
recibirá el mensaje y se encargará de distribuirlo a nuestros
dispositivos conectados.

Esto permite crear multitud de automatizaciones. Por ejemplo:

  • avisarnos cuando finaliza una copia de seguridad;
  • recibir una alerta cuando se produce un error en nuestra página
    web;
  • conocer cuándo termina una tarea programada;
  • avisarnos si queda poco espacio disponible en un servidor;
  • recibir el resultado de un proceso ejecutado mediante
    cron;
  • saber cuándo termina una conversión de vídeo que tarda varias
    horas;
  • recibir una alerta cuando un determinado servicio deja de
    funcionar;
  • conocer cuándo ha finalizado un proceso en nuestro Mac;
  • o recibir desde Windows el resultado de un script de
    mantenimiento.

Slack gratuito y el
límite de aplicaciones

Para realizar este tutorial podemos utilizar un espacio de trabajo
gratuito de Slack.

Conviene distinguir entre una aplicación y un bot. En este artículo crearemos una aplicación personalizada de Slack y dentro de ella utilizaremos un usuario bot para enviar los mensajes.

En el plan gratuito existen límites sobre las aplicaciones e
integraciones que podemos instalar. Por ejemplo, actualmente Slack
establece un límite de 10 aplicaciones de terceros o
aplicaciones personalizadas instaladas
en un espacio de trabajo gratuito.

Podemos crear una única aplicación, por ejemplo, llamada
Notificaciones y utilizar la misma aplicación para enviar notificaciones desde nuestro MacBook, nuestro PC y nuestro hosting web. Incluso podemos crear diferentes canales y desde el programa elegir dónde publicar.

Con este enfoque una única aplicación de Slack puede centralizar las
notificaciones procedentes de muchos sistemas diferentes.

Crear nuestra aplicación en
Slack

El primer paso consiste en crear una aplicación de Slack.

Debemos acceder a la web de la
API de Slack
y crear una nueva aplicación.

Podemos darle un nombre descriptivo como
Notificaciones.

También tendremos que indicar el espacio de trabajo de Slack en el
que queremos instalarla.

Una aplicación de Slack puede disponer de diferentes permisos. Como
en nuestro caso únicamente queremos publicar mensajes, debemos conceder
al bot el permiso OAuth chat:write. Este permiso permite que la aplicación publique mensajes.

Después instalaremos la aplicación en nuestro espacio de trabajo.
Durante el proceso, Slack generará un Bot User OAuth
Token
.

Los tokens de bot suelen comenzar por xoxb-, por ejemplo:

xoxb-XXXXXXXXXXXX-XXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXX

Este token es una credencial secreta.

No debemos publicarlo en una página web, subirlo a un repositorio
público ni incluirlo en un ejemplo que vayamos a compartir con otras
personas.

Cualquier persona que consiga un token válido podría utilizar los
permisos asociados a nuestra aplicación.

Si accidentalmente hacemos público el token, debemos revocarlo y
generar unas credenciales nuevas.

Añadir el bot al canal

También necesitaremos un lugar en el que publicar las
notificaciones.

Podemos crear, por ejemplo, un canal llamado
#notificaciones.

Una vez hecho esto necesitaremos conocer el identificador del
canal.

Los identificadores de canales de Slack tienen un aspecto similar a
C0123456789. Este identificador será el que utilizaremos al llamar a la API.

Slack permite otras formas de organizar y enviar mensajes, pero para
un sistema personal de alertas un canal específico suele ser el enfoque
apropiado.

Cómo se envía un mensaje a
Slack

Slack proporciona el método de su API
chat.postMessage.

La petición se realiza mediante HTTPS utilizando la URL
https://slack.com/api/chat.postMessage.

Debemos enviar nuestro token mediante la cabecera HTTP:

Authorization: Bearer TOKEN

y proporcionar como mínimo el canal y el contenido del mensaje.

Conceptualmente enviaremos un documento JSON parecido a este:

{
    "channel": "C0123456789",
    "text": "Copia de seguridad terminada correctamente"
}

Esta operación es independiente del lenguaje de programación que
utilicemos.

Por esta razón podemos realizar exactamente la misma petición desde
PHP, AppleScript, PowerShell, Python, Java, C# o prácticamente cualquier
entorno capaz de efectuar una petición HTTPS.

Vamos a ver tres ejemplos.

Enviar una
notificación a Slack desde PHP

Supongamos que tenemos una página web alojada en un hosting PHP y
queremos recibir una notificación cuando sucede algún
acontecimiento.

Podría tratarse de una tarea ejecutada mediante cron, una copia de seguridad o un proceso interno de nuestra aplicación.

Una implementación sencilla utilizando cURL sería la siguiente:

<?php

$token = getenv('SLACK_BOT_TOKEN');
$canal = 'C0123456789';

$datos = [
    'channel' => $canal,
    'text' => 'Mensaje enviado desde mi servidor PHP'
];

$curl = curl_init('https://slack.com/api/chat.postMessage');

curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $token,
        'Content-Type: application/json; charset=utf-8'
    ],
    CURLOPT_POSTFIELDS => json_encode($datos)
]);

$respuesta = curl_exec($curl);

curl_close($curl);

echo $respuesta;

El token se obtiene mediante:

getenv('SLACK_BOT_TOKEN')

en lugar de escribirlo directamente en el código.

Siempre que nuestro hosting lo permita, resulta preferible almacenar
credenciales y secretos mediante variables de entorno o mediante algún
mecanismo de configuración situado fuera del directorio público de
nuestra página web.

Crear una función
PHP para enviar mensajes

Si vamos a utilizar Slack desde diferentes partes de nuestra
aplicación PHP, podemos encapsular el código anterior en una
función:

function enviarSlack($mensaje)
{
    $token = getenv('SLACK_BOT_TOKEN');
    $canal = 'C0123456789';

    $datos = [
        'channel' => $canal,
        'text' => $mensaje
    ];

    $curl = curl_init('https://slack.com/api/chat.postMessage');

    curl_setopt_array($curl, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $token,
            'Content-Type: application/json; charset=utf-8'
        ],
        CURLOPT_POSTFIELDS => json_encode($datos)
    ]);

    $respuesta = curl_exec($curl);

    curl_close($curl);

    return $respuesta;
}

Ahora podemos enviar una notificación simplemente escribiendo:

enviarSlack('La copia de seguridad ha terminado correctamente.');

Para enviar una notificación de que se ha terminado la copia de
seguridad sería algo como:

if ($copiaCorrecta) {
    enviarSlack('✅ Copia de seguridad terminada correctamente.');
} else {
    enviarSlack('⚠️ Se ha producido un error durante la copia de seguridad.');
}

Podemos incluir en el mensaje cualquier información que resulte
útil:

enviarSlack(
    "Nuevo pedido recibido.\n" .
    "Número: " . $pedido . "\n" .
    "Importe: " . $importe . " euros"
);

De esta manera, un acontecimiento producido en nuestro hosting puede
generar inmediatamente una notificación en nuestro teléfono.

Enviar una
notificación desde un Mac con AppleScript

El mismo sistema puede utilizarse desde macOS.

AppleScript no necesita disponer de una implementación específica de
la API de Slack. Podemos utilizar el comando curl incluido en macOS mediante do shell script.

Un ejemplo sería:

set slackToken to "xoxb-XXXXXXXXXXXX"
set slackChannel to "C0123456789"
set slackMessage to "El proceso del Mac ha terminado correctamente"

set jsonData to "{\"channel\":\"" & slackChannel & "\",\"text\":\"" & slackMessage & "\"}"

set command to "curl -s -X POST " & ¬
    "-H " & quoted form of ("Authorization: Bearer " & slackToken) & " " & ¬
    "-H " & quoted form of "Content-Type: application/json; charset=utf-8" & " " & ¬
    "--data " & quoted form of jsonData & " " & ¬
    quoted form of "https://slack.com/api/chat.postMessage"

do shell script command

En un script real debemos evitar almacenar el token directamente en
el código cuando exista la posibilidad de que ese archivo pueda ser
compartido.

Una posibilidad es guardarlo en una variable de entorno, un archivo
de configuración protegido o utilizar el llavero de macOS.

Avisarnos cuando
termina un proceso en el Mac

Supongamos que utilizamos nuestro Mac para ejecutar una tarea que
tarda mucho tiempo.

Nuestro AppleScript podría realizar primero el proceso:

do shell script "/ruta/a/mi/programa"

y después enviar el mensaje a Slack:

set slackMessage to "El proceso ha terminado."

De esta manera podemos abandonar el ordenador y recibir una
notificación en el teléfono cuando la tarea haya finalizado.

También podríamos utilizar el resultado del proceso para enviar
mensajes diferentes dependiendo de si la operación ha terminado
correctamente o se ha producido algún error.

Enviar
una notificación desde Windows con un archivo BAT

Windows también puede participar en nuestro sistema de
notificaciones.

Podemos utilizar PowerShell para realizar la petición HTTP e
invocarlo desde un archivo .bat.

Por ejemplo, podemos crear un archivo llamado
notificar.bat con el siguiente contenido:

@echo off

set "SLACK_TOKEN=xoxb-XXXXXXXXXXXX"
set "SLACK_CHANNEL=C0123456789"
set "SLACK_MESSAGE=El proceso de Windows ha terminado"

powershell -NoProfile -Command ^
  "$headers = @{ Authorization = 'Bearer %SLACK_TOKEN%' }; ^
   $body = @{ channel = '%SLACK_CHANNEL%'; text = '%SLACK_MESSAGE%' } | ConvertTo-Json; ^
   Invoke-RestMethod -Uri 'https://slack.com/api/chat.postMessage' -Method Post -Headers $headers -ContentType 'application/json; charset=utf-8' -Body $body"
Ejecutar la
notificación después de otro programa

Una utilidad interesante de los archivos BAT consiste en encadenar
varios comandos.

Por ejemplo:

@echo off

backup.exe

if %ERRORLEVEL% EQU 0 (
    call notificar.bat
)

En este ejemplo se ejecuta primero:

backup.exe

y se consulta posteriormente su código de salida.

Podemos desarrollar el ejemplo para generar una notificación
diferente cuando se produzca un error:

@echo off

backup.exe

if %ERRORLEVEL% EQU 0 (
    set "RESULTADO=La copia de seguridad ha terminado correctamente"
) else (
    set "RESULTADO=ERROR: la copia de seguridad ha fallado"
)

A continuación podemos utilizar el valor de RESULTADO como contenido del mensaje enviado a Slack.

El principio es exactamente el mismo que en PHP y AppleScript:
nuestro programa detecta un acontecimiento y realiza una petición a la
API de Slack.

Comprobar la respuesta de
Slack

Hay un detalle importante que no debemos ignorar en una
implementación real.

No basta con comprobar que la conexión HTTPS se ha realizado
correctamente.

La API de Slack devuelve un documento JSON indicando si la operación
ha tenido éxito.

Una respuesta correcta contendrá un valor equivalente a:

{
    "ok": true
}

Mientras que, si existe algún problema, recibiremos una respuesta
similar a:

{
    "ok": false,
    "error": "..."
}

Por tanto, nuestros scripts deberían comprobar el campo
ok cuando sea importante asegurarnos de que la notificación ha sido aceptada por Slack.

Esto permite detectar situaciones como un token incorrecto, permisos
insuficientes o un canal al que la aplicación no puede acceder.

Seguridad

El elemento más delicado de todo este sistema es el token OAuth del
bot.

Debemos tratarlo igual que una contraseña.

En particular, conviene seguir varias reglas:

  • no publicarlo en GitHub ni en otros repositorios públicos;
  • no incluirlo en artículos, capturas de pantalla o
    documentación;
  • evitar almacenarlo directamente dentro del código;
  • impedir que pueda descargarse desde nuestro servidor web;
  • utilizar variables de entorno o mecanismos seguros de almacenamiento
    de credenciales;
  • revocarlo inmediatamente si sospechamos que ha quedado
    expuesto.

También es aconsejable conceder a la aplicación únicamente los
permisos que necesita.

Para los ejemplos de este tutorial nos interesa utilizar
chat:write.

No existe ninguna razón para conceder acceso a mensajes, usuarios,
archivos u otros recursos si nuestra aplicación solamente necesita
publicar una notificación.

Aplicar el principio del mínimo privilegio reduce las consecuencias
de una posible filtración del token.

No utilizar Slack
como sistema de registro

Aunque podemos enviar mensajes automáticos, Slack no debería
convertirse en el registro completo de actividad de nuestro
servidor.

No sería razonable, por ejemplo, enviar una notificación por cada
petición HTTP recibida por una página web.

Las notificaciones deben reservarse para acontecimientos que
realmente queremos conocer.

El envío masivo puede encontrarse con los límites de frecuencia
establecidos por Slack.

Para almacenar grandes cantidades de acontecimientos debemos utilizar
archivos de registro, bases de datos o plataformas específicas de
monitorización. Slack puede actuar como la última etapa del sistema y
avisarnos únicamente cuando ocurre algo que requiere nuestra
atención.

Limitaciones de la cuenta
gratuita

El plan gratuito de Slack es suficiente para un sistema doméstico o
un pequeño sistema de notificaciones como el descrito en este tutorial,
pero debemos tener presentes sus restricciones.

Slack también establece limitaciones sobre el historial disponible en
los espacios de trabajo gratuitos y aplica límites de frecuencia a las
llamadas realizadas contra su API.

Estas restricciones no suelen representar un problema para un sistema
que envía unas pocas alertas al día, pero son importantes si pretendemos
utilizar Slack para registrar automáticamente una gran cantidad de
acontecimientos.

Usar VoiceOver mientras compartes la pantalla con otro Mac

La administración remota de ordenadores presenta un problema particular para las personas que utilizan un lector de pantalla. No basta con transmitir la imagen del escritorio remoto ni con enviar las pulsaciones del teclado. Para que el usuario pueda trabajar de forma autónoma es necesario que la información de accesibilidad del ordenador remoto también esté disponible para el lector de pantalla.

En MacOS no había una solución estable para realizar esto. Pero con la aparición de MacOS 27 Apple ha incorporado una solución dentro del propio sistema operativo. Cuando un usuario de VoiceOver controla otro Mac mediante la aplicación Compartir Pantalla o mediante Apple Remote Desktop, el lector de pantalla puede activarse temporalmente en el ordenador remoto y permitir la exploración de su interfaz desde el Mac local.

Esta nueva característica está incluida entre las capacidades de accesibilidad disponibles en macOS 27 y representa una mejora especialmente relevante para usuarios ciegos que necesitan realizar tareas de soporte, administración o asistencia remota.

El problema de utilizar un lector de pantalla a través de una conexión remota

Una sesión de pantalla compartida está diseñada fundamentalmente para transmitir una representación visual de un escritorio remoto y permitir que otro ordenador envíe órdenes mediante el teclado y el dispositivo apuntador (ratón o trackpad). La mayoría de aplicaciones enfocadas en facilitar el acceso remoto a un dispositivo lo que hacen es una captura de imagen contínua y la envían al dispositivo controlador.

Esta forma de realizar la compartición de escritorio presenta limitaciones evidentes para alguien que depende de un lector de pantallas.

Un lector de pantalla no trabaja simplemente describiendo una imagen de la pantalla. VoiceOver consulta la infraestructura de accesibilidad de macOS para identificar ventanas, botones, campos de texto, tablas, barras de herramientas y otros objetos de la interfaz. También necesita conocer sus nombres, estados, relaciones jerárquicas y posibles funciones.

Disponer de la imagen del escritorio remoto no equivale a disponer de acceso a su interfaz de accesibilidad.

La solución adoptada por Apple consiste en integrar VoiceOver con el mecanismo de pantalla compartida para que el lector pueda trabajar temporalmente con el entorno accesible del ordenador remoto.

VoiceOver se activa temporalmente en el Mac remoto

Cuando se utiliza VoiceOver para controlar otro Mac usando Apple Remote Desktop o la aplicación Compartir Pantalla, macOS puede activar temporalmente VoiceOver en el ordenador remoto.

Este comportamiento no modifica de forma permanente la configuración de VoiceOver del equipo controlado.

Según explica Apple en la documentación de compartir pantalla con VoiceOver, cuando termina la sesión de pantalla compartida se restauran los ajustes originales del dispositivo remoto.

Esto permite utilizar las funciones necesarias para proporcionar accesibilidad durante la conexión sin dejar VoiceOver activado posteriormente ni alterar de manera permanente la configuración habitual del otro usuario.

Desde el punto de vista del diseño de accesibilidad, se trata de una decisión importante. El estado de accesibilidad necesario para la sesión pertenece a la propia sesión y no se convierte automáticamente en una modificación persistente del sistema remoto.

El Mac remoto no empieza a hablar

Activar VoiceOver temporalmente en el ordenador remoto podría provocar una situación problemática si ambos ordenadores comenzasen a reproducir simultáneamente la misma información.

Apple evita este comportamiento haciendo que VoiceOver no reproduzca sonidos ni muestre sus elementos visuales en el dispositivo remoto durante este tipo de sesión.

La salida utilizada por la persona que controla el ordenador se mantiene, por tanto, en el Mac local.

Esta separación resulta especialmente útil en situaciones de soporte técnico. Una persona ciega puede controlar mediante VoiceOver el ordenador de otra persona sin provocar necesariamente que el Mac remoto empiece a verbalizar toda la interfaz a través de sus propios altavoces.

También reduce la duplicación de información y facilita determinar qué sistema está produciendo cada respuesta.

Un cambio de tono diferencia el Mac local del remoto

Trabajar simultáneamente con dos sistemas hace que el usuario necesite saber si VoiceOver está describiendo la interfaz de su propio ordenador o la del ordenador remoto.

Para proporcionar esta referencia, VoiceOver modifica el tono de la voz cuando se interactúa con el dispositivo remoto.

En lugar de depender exclusivamente de mensajes adicionales que interrumpirían constantemente la navegación, VoiceOver utiliza una propiedad de la propia síntesis de voz para comunicar que se ha cambiado de contexto.

El usuario puede así reconocer auditivamente si se encuentra navegando por la interfaz local o por el Mac que está controlando.

Activación de la accesibilidad al comenzar una sesión

Cuando VoiceOver está activo en el Mac local y se inicia una sesión compatible de pantalla compartida, macOS solicita automáticamente que se activen los ajustes de accesibilidad necesarios en el equipo remoto.

Una vez concedido este acceso, el usuario puede desplazarse con VoiceOver hasta la pantalla compartida.

La pantalla remota se presenta como un elemento con el que es necesario interactuar, de forma similar a una tabla, un grupo u otros elementos complejos de la interfaz de macOS.
Para comenzar a explorar su contenido se utiliza el comando:
VO + Mayúsculas + Flecha abajo

Después de comenzar la interacción con la pantalla compartida, los comandos habituales de navegación de VoiceOver pueden utilizarse para recorrer la interfaz del ordenador remoto.

Esto significa que la sesión remota deja de ser únicamente una superficie visual dentro de una ventana y pasa a comportarse como un entorno accesible con el que VoiceOver puede interactuar.

Una herramienta especialmente útil para soporte técnico

Con esta nueva herramienta la posibilidad de proporcionar asistencia remota utilizando VoiceOver ya es posible de forma sencilla y sin necesidad de ninguna aplicación o servicio extra.

Apple Remote Desktop, por ejemplo, está diseñado específicamente para administrar equipos Mac de forma remota. La posibilidad de utilizar VoiceOver durante estas sesiones hace que una parte importante de esas tareas pueda realizarse sin depender de la inspección visual de la pantalla remota.

La accesibilidad deja así de estar limitada al ordenador situado físicamente delante del usuario y pasa a formar parte del propio mecanismo de uso remoto.

Garantizar la accesibilidad con pruebas automatizadas

En el mundo del desarrollo de software una funcionalidad que hoy funciona puede dejar de hacerlo mañana después de una modificación aparentemente inocente. Para evitar este problema se crean las pruebas automatizadas para mantener la funcionalidad y la forma de utilizar una interfaz de usuario.

La accesibilidad debería formar parte de esas pruebas para garantizar que todos los usuarios pueden acceder a la funcionalidad.

Sin embargo, todavía es frecuente tratarla como una actividad posterior al desarrollo. Primero se construye la interfaz, después se comprueba si funciona y, en algún momento, alguien ejecuta una herramienta de accesibilidad o realiza una auditoría. El problema de este planteamiento es que convierte la accesibilidad en una fotografía puntual cuando debería ser una propiedad del software que intentamos conservar durante toda su vida.

La automatización de las pruebas puede ayudar a garantizar y mantener esa accesibilidad.

Como explica Marcy Sutton en Writing Automated Tests for Accessibility, las pruebas automáticas no pueden descubrir todos los problemas de accesibilidad. Tampoco sustituyen las pruebas manuales ni las realizadas con personas con discapacidad. Su utilidad está en otro lugar: permiten comprobar de manera sistemática aquello que una máquina sí puede verificar y, sobre todo, detectar regresiones antes de que lleguen a producción.

La accesibilidad también puede sufrir regresiones

Una regresión aparece cuando una modificación introduce un problema en algo que anteriormente funcionaba.

Si una función debe devolver un determinado resultado para una entrada concreta, podemos escribir una prueba que establezca ese comportamiento. Cada vez que alguien modifique el código, la prueba volverá a ejecutarse.

Con la accesibilidad sucede exactamente lo mismo. Un botón puede tener hoy un nombre accesible y perderlo durante una refactorización. Un diálogo puede devolver correctamente el foco al elemento que lo abrió hasta que alguien modifica su implementación. Un componente de pestañas puede responder a las flechas del teclado y dejar de hacerlo después de actualizar una dependencia.

La interfaz puede parecer idéntica. Para una persona que utiliza el ratón puede incluso continuar funcionando con normalidad. Pero para otros usuarios el cambio puede haber roto una parte esencial de la aplicación.

Las pruebas automatizadas permiten convertir algunos de esos requisitos de accesibilidad en condiciones verificables. En lugar de confiar únicamente en que nadie elimine accidentalmente un comportamiento accesible, podemos hacer que el propio proyecto compruebe su existencia.

Automatizar no significa automatizarlo todo

La accesibilidad no puede reducirse a obtener un informe sin errores de una herramienta automática.

Una máquina puede comprobar determinadas propiedades del código y del documento. Puede encontrar ciertos usos incorrectos de ARIA, detectar algunos problemas de contraste, analizar etiquetas o identificar errores en la estructura HTML. También podemos escribir nuestras propias pruebas para comprobar el comportamiento de componentes concretos.

Pero existen preguntas mucho más difíciles.

¿Tiene sentido el orden de lectura? ¿Es comprensible el texto alternativo de una imagen dentro de su contexto? ¿Resulta sencillo completar una tarea utilizando un lector de pantalla? ¿Es predecible la gestión del foco? ¿Puede una persona comprender un mensaje de error y recuperarse de él?

No todas estas cuestiones pueden resolverse observando el DOM y aplicando un conjunto de reglas.

La automatización, por tanto, no debería entenderse como un sustituto de las pruebas de accesibilidad realizadas por personas. Es una capa adicional dentro de una estrategia más amplia.

Su ventaja consiste precisamente en encargarse de las comprobaciones repetibles para que el tiempo humano pueda dedicarse a aquello que exige interpretación, experiencia y contexto.

Probar resultados en lugar de implementaciones

Una buena prueba automatizada debería sobrevivir, dentro de lo razonable, a los cambios internos del software.
Las pruebas deberían concentrarse en el resultado esperado y no en los detalles de implementación.

Supongamos que tenemos un componente que muestra un diálogo. Desde el punto de vista de quien utiliza la interfaz, lo importante no es qué funciones internas se ejecutan ni qué variables cambian de valor. Lo que importa es que el diálogo pueda abrirse con el teclado, que el foco termine donde corresponde, que pueda cerrarse y que, al hacerlo, el usuario pueda continuar desde una posición coherente.

Una prueba demasiado vinculada a la implementación puede romperse cada vez que refactorizamos el componente, aunque su comportamiento siga siendo correcto. Cuando esto ocurre con frecuencia, las pruebas empiezan a percibirse como un obstáculo. Finalmente alguien las desactiva o las elimina.

Una prueba centrada en el comportamiento expresa algo diferente: mientras esta funcionalidad exista, esta propiedad debe seguir cumpliéndose.

En accesibilidad, esa diferencia es particularmente valiosa porque transforma ciertos requisitos en parte del contrato del componente.

El teclado como parte de las pruebas

El teclado es una de las herramientas más sencillas para comenzar a evaluar la accesibilidad de una interfaz.

Podemos recorrer una página utilizando la tecla Tab, comprobar si alcanzamos los controles interactivos, observar si el foco resulta visible y verificar si podemos realizar una tarea sin recurrir al ratón.

Una parte de ese comportamiento también puede incorporarse a las pruebas automatizadas.

Un diálogo puede tener una prueba que compruebe que Escape permite cerrarlo. Un menú puede verificar el funcionamiento de las teclas de dirección. Un componente de pestañas puede comprobar que el foco se desplaza de acuerdo con el patrón de interacción previsto.

Esto resulta especialmente interesante porque la accesibilidad deja de estar asociada únicamente al marcado HTML.

Un componente puede utilizar atributos ARIA aparentemente correctos y seguir siendo inaccesible si su comportamiento con el teclado es incorrecto. La accesibilidad de una interfaz dinámica depende tanto de la información que exponemos como de las interacciones que permitimos realizar.

Las pruebas de integración son especialmente adecuadas para muchas de estas comprobaciones, ya que el foco y la interacción suelen atravesar los límites de un único componente.

Pruebas unitarias y pruebas de integración

No todas las pruebas de accesibilidad tienen que ejecutarse al mismo nivel.

Una prueba unitaria puede comprobar una pieza aislada del sistema. Por ejemplo, que determinada propiedad proporcionada a un componente termine convirtiéndose en el nombre accesible de un botón.

Este tipo de prueba es rápida y permite verificar problemas concretos.

Las pruebas de integración permiten observar un escenario más amplio. Podemos cargar una interfaz, simular una interacción con el teclado y comprobar qué elemento recibe el foco después de una determinada acción.

Las pruebas de extremo a extremo llevan esta idea todavía más lejos, reproduciendo recorridos próximos a los de una persona que utiliza la aplicación.

No existe un único nivel correcto para probar la accesibilidad. La elección depende de aquello que queremos garantizar.

Si nos interesa comprobar que una API transmite correctamente determinada información de accesibilidad, una prueba unitaria puede ser suficiente. Si queremos saber qué sucede con el foco después de abrir y cerrar varios componentes, necesitaremos observar una porción mayor de la aplicación.

Incorporar motores de accesibilidad

Además de escribir pruebas específicas para nuestros componentes, podemos incorporar motores especializados en detectar automáticamente problemas de accesibilidad.

Uno de los más conocidos es axe-core, desarrollado por Deque. Puede integrarse con distintos entornos de pruebas para analizar un componente, una página o un estado determinado de la aplicación.

La idea es sencilla. La prueba presenta una interfaz al motor de accesibilidad, ejecuta el análisis y comprueba los resultados obtenidos. Si aparecen determinadas infracciones, la prueba puede fallar.

Esto permite trasladar algunas comprobaciones al mismo lugar en el que ya verificamos el resto del software.

En lugar de esperar a una auditoría posterior, una modificación que introduzca ciertos errores puede provocar un fallo durante la integración continua. El problema aparece entonces mucho más cerca del momento en que fue introducido.

Esta proximidad tiene una ventaja práctica considerable. Corregir un error cuando todavía estamos trabajando en el componente suele ser más sencillo que descubrirlo semanas después, cuando el código ya se encuentra integrado con otras partes del sistema.

Probar los estados, no solamente las páginas

Las aplicaciones web modernas cambian constantemente de estado. Un menú cerrado no contiene necesariamente los mismos elementos visibles que un menú abierto. Un diálogo puede no existir en el DOM hasta que alguien pulsa un botón. Un formulario puede mostrar mensajes adicionales después de una validación. Un acordeón oculta información hasta que se expande.

Todo esto plantea un problema para las herramientas automáticas: solo pueden analizar aquello que existe en el estado que les presentamos.
Ejecutar un análisis de accesibilidad inmediatamente después de cargar una página puede dejar fuera una parte considerable de la interfaz.

Por esta razón las pruebas automáticas deberían recorrer los estados significativos.

Podemos analizar la página inicialmente, abrir después un diálogo y volver a analizarla. Podemos desplegar un menú, provocar los errores de un formulario o mostrar contenido que inicialmente estaba oculto.

La unidad de accesibilidad que nos interesa comprobar no siempre es la página. En una aplicación dinámica, muchas veces es el estado de la interfaz.

Accesibilidad dentro de la integración continua

Cuando estas pruebas forman parte de la ejecución habitual del proyecto, la accesibilidad entra también en el proceso de integración continua.

Una auditoría periódica nos dice qué problemas existen en un momento determinado. Una prueba automática puede impedir que ciertos problemas conocidos vuelvan a introducirse.

La diferencia es parecida a la que existe entre inspeccionar periódicamente si una puerta permanece cerrada e instalar un mecanismo que avise cada vez que alguien la deja abierta.

La segunda opción tampoco garantiza que el edificio sea seguro. Pero convierte una condición concreta en algo que se vigila continuamente.

Con la accesibilidad podemos hacer lo mismo. No podremos automatizar toda la experiencia de una persona, pero sí establecer determinadas condiciones que el software no debería incumplir.

El peligro de confiar demasiado en el resultado

Existe una consecuencia incómoda al automatizar la accesibilidad que consiste en que podemos empezar a confiar demasiado en aquello que hemos automatizado.

Una batería de pruebas sin errores no demuestra que un producto sea accesible. Más bien demuestra que ha superado las comprobaciones que hemos decidido ejecutar.

Las herramientas automáticas trabajan con reglas que pueden evaluarse mediante software. La experiencia de una persona utilizando una interfaz es considerablemente más compleja. Un sitio puede superar todas las comprobaciones automáticas y continuar presentando obstáculos importantes. Por esta razón las pruebas manuales siguen siendo necesarias.

Utilizar únicamente el teclado, probar con lectores de pantalla, comprobar diferentes niveles de ampliación, observar el comportamiento en dispositivos móviles y estudiar la experiencia con distintas tecnologías de apoyo permiten encontrar problemas que difícilmente aparecerán en una comprobación automática.

Además de todo esto deberíamos probar con personas.

Se puede comprobar técnicamente que un componente sigue un patrón accesible y descubrir después que resulta confuso durante una tarea real. Cumplimiento técnico y usabilidad están relacionados, pero no son equivalentes.

Automatizar para reservar tiempo para las personas

El objetivo final de automatizar las pruebas de accesibilidad no debería ser eliminar la intervención humana, sino utilizarla mejor.

Hay comprobaciones que una máquina puede repetir cientos de veces con un coste muy pequeño. No tiene demasiado sentido pedir a una persona que las realice manualmente en cada modificación si podemos convertirlas en pruebas fiables.

Ese tiempo puede dedicarse a cuestiones que necesitan criterio humano.

La automatización también proporciona una memoria que los equipos de desarrollo no siempre tienen. Las personas cambian de proyecto, los componentes evolucionan y las decisiones tomadas meses atrás pueden olvidarse. Una prueba permanece junto al código recordando que determinado comportamiento no es accidental.

Si alguien implementó correctamente la gestión del foco de un diálogo, una prueba puede conservar esa decisión mucho después de que se haya olvidado la conversación que la originó.

La accesibilidad como propiedad del software

Cuando escribimos una prueba para una característica de accesibilidad estamos diciendo que ese comportamiento forma parte del producto.
No es una mejora opcional que comprobaremos al final. No es una característica externa al código. Es una condición que esperamos que siga siendo cierta después de cada modificación.

La automatización no puede decirnos que una aplicación es accesible. Tampoco puede sustituir a una persona utilizando un lector de pantalla, navegando con el teclado o intentando completar una tarea real.

Lo que si puede hacer es impedir que olvidemos automáticamente algunas de las cosas que ya habíamos aprendido a hacer bien.

Una estrategia madura de accesibilidad combina esas capas. Automatiza aquello que puede expresarse mediante reglas y comportamientos verificables, utiliza pruebas manuales para explorar lo que necesita interpretación y recurre a personas con discapacidad para conocer la experiencia que ningún conjunto de aserciones puede reproducir.

Las pruebas automatizadas no son el destino de la accesibilidad. Son una forma de evitar que, mientras avanzamos, deshagamos parte del camino recorrido.

Una encuesta mundial busca conocer la situación de la alfabetización braille

El Consejo Internacional para la Educación de las Personas con Discapacidad Visual (ICEVI) y la Unión Mundial de Ciegos (UMC), junto con otras organizaciones y profesionales vinculados a la discapacidad visual, han puesto en marcha la Encuesta Mundial sobre Alfabetización Braille, una iniciativa destinada a conocer la situación actual del aprendizaje y el uso del braille en diferentes países.

La encuesta está dirigida a personas ciegas mahyores de edad, con baja visión o con sordoceguera. También pueden participar padres, madres y tutores legales de menores con estas discapacidades, profesionales relacionados con la enseñanza del braille y personas dedicadas a la transcripción de contenidos a braille.

La encuesta, en inglés Global Braille Survey, forma parte de la campaña internacional More Braille: More Empowerment y busca recoger información sobre cómo el braille se aprende, se enseña y se utiliza y las posibilidades de acceso a este sistema de lectoescritura.

El estudio busca identificar las principales dificultades relacionadas con la alfabetización braille. Los datos obtenidos podrán servir para conocer mejor las distintas realidades existentes y contribuir al desarrollo de iniciativas, recursos, investigaciones y políticas relacionadas con el acceso al braille.

El cuestionario es accesible y se encuentra disponible en varios idiomas, entre ellos el castellano.

Quienes deseen participar pueden acceder directamente a la Encuesta Mundial sobre Alfabetización Braille.

Participación en AliBlueBox en el día mundial del videojuego

El pasado 29 de agosto participé en el canal de AliBlueBox en una entrevista donde hablé con Alicia sobre accesibilidad, videojuegos y Vibecoding.

La IA como oportunidad de crear con más accesibilidad y más rapidez

Hablamos de muchos temas siempre sobre tecnología y accesibilidad y, con el videojuego como hilo argumental, aproveché para presentar mi próximo juego: Boat hunter, un juego arcade donde pilotas un barco de guerra por el océano Atlántico buscando barcos enemigos a los que hundir.
Puedes ver la entrevista en AliBlueBox en Youtube.

La democratización de la creación de software y la IA

La historia del desarrollo de software consiste en un proceso continuo de reducción de barreras. Cada avance tecnológico relevante ha facilitado que un número mayor de personas pueda utilizar ordenadores para crear nuevas herramientas. Pero reducir la dificultad necesaria para crear aplicaciones y servicios no implica eliminar la necesidad de comprender cómo se construye un proyecto software.

Superando la barrera del hardware

Durante las primeras décadas de la informática, crear software estaba restringido a organizaciones capaces de acceder a ordenadores muy costosos y a profesionales con una formación muy especializada. La aparición del ordenador personal modificó esta situación. Una persona podía adquirir para su casa una máquina programable de propósito general por un precio relativamente asequible.

La barrera de entrada pasó entonces a estar formada principalmente por tres elementos: disponer de un ordenador personal, acceder a las herramientas necesarias para desarrollar programas y adquirir los conocimientos técnicos para utilizarlas. Un ordenador personal, un compilador y suficiente documentación podían constituir un entorno de desarrollo completo. El hardware se había democratizado, pero el conocimiento seguía siendo una barrera considerable.

Internet y la democratización del conocimiento

La popularización de Internet produjo una nueva transformación. El ordenador personal había democratizado el acceso a la capacidad de computación, Internet comenzó a democratizar el acceso al conocimiento necesario para utilizarla.

La documentación técnica, que anteriormente podía encontrarse principalmente en libros, manuales, revistas especializadas, universidades y entornos profesionales, empezó a estar disponible en línea. Posteriormente aparecieron foros, comunidades de desarrolladores, proyectos de código abierto, cursos, tutoriales, blogs técnicos y plataformas especializadas en preguntas y respuestas.

Aprender a programar continuaba requiriendo un esfuerzo considerable, pero el coste económico y la dificultad de encontrar información se habían reducido drásticamente. Un desarrollador que encontraba un problema podía buscar su mensaje de error, consultar la documentación de una biblioteca o estudiar cómo otras personas habían resuelto situaciones similares.

Internet facilitó el aprendizaje de lenguajes de programación y ayudó a la difusión de conocimientos sobre arquitectura de software, patrones de diseño, seguridad, pruebas, bases de datos, sistemas distribuidos y metodologías de desarrollo.

La barrera del conocimiento no desapareció, pero acceder a él se hizo progresivamente más sencillo.

De buscar información a conversar con ella

La aparición de los asistentes conversacionales basados en inteligencia artificial introdujo un cambio cualitativamente diferente. El problema dejó de ser únicamente encontrar información para convertirse también en poder interrogarla mediante lenguaje natural.

Ante una cuestión técnica, ya no era imprescindible formular correctamente una búsqueda, localizar varias fuentes, interpretarlas y construir una respuesta a partir de ellas. Un chatBot con conocimientos de ingeniería del software podía realizar parte de ese proceso y proporcionar directamente una explicación adaptada al contexto presentado por el usuario.

Conceptos que requerían consultar diferentes libros, artículos o discusiones técnicas pueden explorarse mediante una conversación en un chat. Con este avance era posible describir un problema, solicitar alternativas, preguntar por sus ventajas e inconvenientes y profundizar sucesivamente en determinados aspectos del problema.

La inteligencia artificial comenzó así a actuar como una nueva capa de abstracción sobre el conocimiento técnico disponible.

Pero facilitar el acceso a una respuesta no equivale a comprender la solución que se ha obtenido. Una respuesta técnicamente plausible puede contener errores, asumir condiciones inexistentes o ser adecuada para un contexto diferente. La capacidad para evaluar la respuesta continúa dependiendo del conocimiento de la persona que la recibe.

Del asistente al agente

La reciente evolución de la IA introduce un cambio todavía más profundo. Este cambio consiste en que las herramientas de inteligencia artificial han dejado de limitarse a explicar cómo escribir software y han comenzado a participar directamente en su construcción.

Los modelos actuales pueden generar código, modificar proyectos existentes, ejecutar herramientas, interpretar errores, crear pruebas y realizar sucesivas modificaciones hasta obtener aparentemente el resultado solicitado. La aparición de agentes de programación amplía todavía más esta capacidad al permitir que determinados procesos se ejecuten con un grado creciente de autonomía.

En este contexto se ha popularizado el concepto de vibeCoding, una forma de creación de software en la que el usuario describe mediante lenguaje natural aquello que desea delegando  parte de las decisiones de implementación en la inteligencia artificial.

Esta capacidad está produciendo dos interpretaciones aparentemente contradictorias. Hay personas sin conocimientos avanzados de programación que ya pueden construir prototipos y aplicaciones que anteriormente habrían requerido la intervención de un desarrollador. Por otra parte, los profesionales del software pueden utilizar estas mismas herramientas para aumentar considerablemente su productividad.

La consecuencia es una nueva reducción de la barrera de entrada a la creación de software.

La falsa sensación de éxito

Esta democratización presenta un problema fundamental que consiste en que generar software que aparentemente funciona es considerablemente más sencillo que generar buen software.

Cuando una persona solicita a una inteligencia artificial que construya una aplicación, el sistema intenta materializar la descripción recibida. Si la especificación es incompleta, ambigua o técnicamente inconsistente, el modelo debe trabajar con esa incertidumbre. Puede realizar suposiciones razonables y producir un resultado visualmente convincente, pero eso no significa que haya construido exactamente aquello que el usuario necesitaba.

Una característica especialmente problemática del vibeCoding es que tiene una facilidad para producir una sensación prematura de éxito.

Una interfaz puede mostrarse correctamente. Un botón puede ejecutar la acción esperada. Los datos pueden almacenarse y recuperarse. Desde el punto de vista del usuario, el software parece terminado.

Sin embargo, gran parte de la calidad del software no resulta inmediatamente visible.

La arquitectura puede ser inadecuada para futuras ampliaciones. El modelo de datos puede contener decisiones que dificulten su evolución. Pueden existir problemas de concurrencia, seguridad o rendimiento. El tratamiento de errores puede ser insuficiente. Las pruebas pueden cubrir únicamente los escenarios más evidentes. Las dependencias pueden haber sido elegidas sin considerar sus consecuencias a largo plazo.

El problema no consiste necesariamente en que la inteligencia artificial haya ejecutado incorrectamente la tarea. Puede haber construido de manera razonable aquello que se le solicitó. El problema puede encontrarse en en la incapacidad del usuario para describir con suficiente precisión aquello que realmente necesitaba.

Especificar también es ingeniería

El desarrollo profesional de software nunca ha consistido exclusivamente en escribir código. Una parte importante del trabajo consiste en transformar necesidades ambiguas en requisitos concretos, identificar restricciones, anticipar situaciones excepcionales y decidir qué compromisos son aceptables.

Cuando se delega la implementación en un agente de inteligencia artificial, estas actividades no desaparecen, más bien adquieren una importancia mayor.

Una especificación insuficiente produce un número de soluciones demasiado amplio. El sistema debe completar la información que falta mediante inferencias. Cuantas más decisiones se deleguen, mayor será la probabilidad de que el resultado se aleje de las necesidades reales.

En este sentido, los modelos generativos pueden reducir drásticamente el coste de escribir código sin reducir en la misma proporción la dificultad de especificar correctamente un sistema.

La programación puede hacerse más accesible mientras que la ingeniería de software continúa siendo difícil.

El problema aparece después de que el programa funcione

Existe además una dimensión temporal que puede pasar inadvertida durante la generación inicial. El coste de un producto de software no termina cuando se obtiene su primera versión funcional.

El software necesita ser corregido, actualizado, ampliado, auditado y adaptado a nuevos requisitos. En sistemas con una vida útil suficientemente larga, una parte considerable del esfuerzo se dedica precisamente a comprender y modificar código existente.

El código generado automáticamente no está exento de esta realidad.

Una sucesión de instrucciones destinadas únicamente a conseguir que el sistema vuelva a funcionar puede introducir duplicaciones, dependencias innecesarias, soluciones locales incompatibles con la arquitectura general o abstracciones difíciles de comprender. Cada modificación puede resolver el problema inmediato y, al mismo tiempo, aumentar la dificultad de implementar la siguiente versión del producto.

El riesgo no es sólo producir código incorrecto. También existe el riesgo de producir código que nadie comprende suficientemente bien como para sustituir el trabajo de la IA por código mantenible, escalable y actualizable.

La mantenibilidad del proyecto se convierte en una cuestión central. Si la persona que dirige el desarrollo no comprende la arquitectura creada por la IA, evaluar una modificación propuesta por otro agente de IA resulta difícil. La capacidad de generar cambios puede crecer más rápidamente que la capacidad para evaluar sus consecuencias.

La productividad y la desaparición del equipo

El incremento de productividad asociado a estas herramientas también está modificando la percepción sobre la organización de los equipos de desarrollo. Un profesional experimentado asistido por inteligencia artificial puede realizar determinadas tareas que anteriormente requerían muchas más horas de trabajo. Esto puede generar la impresión de que algunas funciones, especialmente las posiciones junior, han dejado de ser necesarias o de que equipos completos pueden ser sustituidos por un desarrollador experimentado acompañado de varios agentes.

Esta conclusión confunde capacidad de producción con capacidad organizativa.

Un equipo de software no está únicamente para producir líneas de código. También está para distribuir conocimiento, revisar decisiones, detectar errores, cuestionar hipótesis e ideas y permitir que diferentes personas desarrollen experiencia sobre el sistema. De la misma forma un desarrollador junior no es simplemente una versión menos productiva de un desarrollador senior.

Las posiciones junior forman parte del mecanismo para formar los profesionales experimentados del futuro.

Si las organizaciones eliminan sistemáticamente esas posiciones porque la inteligencia artificial permite que los profesionales actuales produzcan más software, pueden obtener una mejora de productividad inmediata mientras debilitan simultáneamente su capacidad para formar nuevas generaciones de especialistas.

La cuestión no es cuántas personas son necesarias para producir una determinada cantidad de software, sino qué conocimientos deben existir dentro de una organización para comprender, mantener y evolucionar ese software durante años.

De pedir resultados a pedir conocimiento

La democratización de la creación de software mediante inteligencia artificial ofrece una oportunidad que puede ser más importante que la generación automática de código.

Las mismas herramientas que permiten solicitar crea esta aplicación permiten adoptar un enfoque diferente como pedir enséñame a construir esta aplicación.

La diferencia entre ambas formas es sustancial.

En el primer caso, en el que la persona pide la creación, el objetivo principal es obtener un resultado. En el segundo caso, el resultado se convierte también en un mecanismo de aprendizaje. La inteligencia artificial puede explicar las decisiones arquitectónicas, presentar alternativas, justificar la elección de determinadas estructuras de datos, generar ejemplos progresivos, analizar errores y ayudar al usuario a comprender las consecuencias de cada modificación.

De esta forma, la inteligencia artificial puede utilizarse como sustituto parcial de determinadas tareas de programación, y como un instrumento para acelerar la adquisición de conocimientos. La misma tecnología que permite programar sin comprender puede convertirse en una de las herramientas más eficaces para aprender a comprender.

Una nueva etapa de la democratización

Puede ser cada vez más sencillo conseguir que un ordenador produzca una aplicación. Pero sigue  siendo difícil determinar qué aplicación debe construirse, cómo debe comportarse en situaciones no previstas, qué arquitectura permitirá mantenerla, qué riesgos introduce y cómo deberá evolucionar cuando cambien sus requisitos.

La democratización de la creación de software no debería entenderse como el final de la necesidad de aprender ingeniería de software. Mas bien puede ser lo contrario.

Cuando producir código deja de ser el principal cuello de botella, comprender qué código merece la pena producir, evaluar el que ha sido generado y ser capaz de mantenerlo adquiere más importancia.

El futuro de la creación de software no estará en elegir entre programar manualmente o delegar la programación en una inteligencia artificial. Más bien estará en utilizar la inteligencia artificial para elevar progresivamente el nivel de abstracción al que trabajan las personas sin renunciar al conocimiento necesario para comprender aquello que están construyendo.

La herramienta puede escribir cada vez más código pero la responsabilidad de entender el sistema continúa siendo humana.