Anunciando el beta de null safety de Dart

Comenzando el viaje de migrar paquetes a un estado sound y más seguro

Cross-posted here and on the Blog de Flutter.

Hoy anunciamos que sound null safety está disponible en beta para Dart y Flutter. Null safety es nuestra última gran función de productividad, diseñada para ayudarte a evitar errores de null, una clase de bugs que suelen ser difíciles de detectar. Si buscas un resumen rápido de por qué estamos entusiasmados con null safety, mira este nuevo video:

Mira en YouTube en una nueva pestaña: "Null safety en Dart - Introducción"

Con null safety pasando a beta, es hora de comenzar la migración comunitaria de los miles de paquetes disponibles en pub.dev. Hemos migrado las bibliotecas centrales de Dart, el framework de Flutter, y más de 40 paquetes de Dart y Flutter. Con esto esperamos ver a la comunidad adoptar null safety migrando sus paquetes.

Cronograma de sound null safety de Dart mostrando previews técnicas en junio y octubre de 2020, beta en noviembre de 2020, y estable planeado para principios de 2021.

Con la beta, también estamos comenzando la recta final antes de lanzar una versión estable de la función null safety. Esperamos que uses la función y nos cuentes si se pueden mejorar partes de ella, si los mensajes de la interfaz pueden hacerse más fáciles de entender, o si la documentación puede ser más clara. Estamos realmente esperando tus comentarios.

Optar por null safety

#

Antes de hablar sobre la migración a null safety, es importante repetir que (como se indica en nuestros principios de null safety) tú tienes el control de cuándo comenzar la adopción de null safety. Las apps y los paquetes solo se ejecutarán con null safety si su restricción mínima del SDK de Dart es al menos una prerelease de Dart 2.12:

environment:
  sdk: "**>=2.12.0-0** <3.0.0"

Para experimentarlo, intenta crear una pequeña app hello con null safety (por ejemplo, usando dart create) que contenga código como el que se muestra a continuación. Luego puedes intentar ejecutar la app antes y después de cambiar la restricción del SDK y ejecutar dart pub get, y experimentar cómo cambia el comportamiento del programa. (Asegúrate de usar un SDK que reporte 2.12 en dart --version.)

***bin/hello.dart:***
...
void main() {
  var hello = 'Hello Dart developers';
  if (someCondition) {
    hello = null;
  }
  print(hello);
}

Before changing the SDK constraint:
$ dart run

null

After changing the SDK constraint (and running dart pub get):
$ dart run

bin/hello.dart:6:13: Error: Null can't be assigned to a variable of type 'String' because 'String' is not nullable.

    hello = null;
            ^

Migrar a null safety

#

Para migrar un paquete (o app sencilla) a null safety, sigue estos cinco pasos, que están completamente documentados en la guía de migración en dart.dev.

Paso 1: Verifica si tus dependencias están listas

#

Recomendamos fuertemente migrar el código en orden, migrando primero las hojas del grafo de dependencias. Por ejemplo, si C depende de B que depende de A, migra primero A a null safety, luego B, luego C. Este orden aplica ya sea que A, B y C sean librerías, paquetes o apps.

Un gráfico de dependencias mostrando el orden de migración de paquetes a null safety de abajo hacia arriba.

¿Por qué es importante el orden? Aunque puedes hacer algo de progreso migrando código antes de que tus dependencias migren, corres el riesgo de tener que hacer una segunda pasada de migración si tus dependencias cambian sus APIs durante su migración. Si algunas de tus dependencias no son null safe, considera contactar a los publicadores de los paquetes usando los detalles de contacto listados para cada paquete en pub.dev.

Verificar que las dependencias estén listas

#

Para verificar si tu app o paquete está listo para comenzar la migración, puedes usar dart pub outdated en modo null safety. El ejemplo a continuación muestra que esta app está lista para migrar si actualiza sus dependencias a las versiones prerelease de path, process, y pedantic como se indica en la columna Resolvable.

Salida de terminal del comando dart pub outdated mostrando el estado de migración de null safety.

Si el soporte de null safety está disponible en versiones menores nuevas, las verás en la columna Upgradable. A menudo el soporte de null safety estará disponible en versiones mayores nuevas; en ese caso verás las versiones listadas bajo Resolvable en la salida de outdated. Para actualizar a esas versiones, edita tu archivo pubspec.yaml para permitir esas versiones mayores. Por ejemplo, podrías cambiar process: ^3.0.13 a process: ^4.0.0-nullsafety.

También puedes encontrar paquetes con soporte de null safety en pub.dev usando las nuevas etiquetas de Null safety en las páginas de los paquetes (por ejemplo collection 1.15) y una nueva opción de búsqueda de null safety en la búsqueda avanzada.

Interfaz de búsqueda de pub.dev con el filtro de null safety activado bajo las opciones de búsqueda avanzada.

Paso 2: Migra usando la herramienta de migración

#

Si tus dependencias están listas, puedes proceder a migrar tu app o paquete usando la herramienta de migración, dart migrate.

La herramienta de migración es interactiva, así puedes revisar las propiedades de nulabilidad que la herramienta ha inferido. Si no estás de acuerdo con alguna de las conclusiones de la herramienta, puedes añadir pistas de nulabilidad para cambiar la inferencia. Añadir unas pocas pistas de migración puede tener un gran impacto en la calidad de la migración.

La interfaz interactiva de la herramienta dart migrate mostrando los cambios de código propuestos.

Hemos tenido un pequeño número de autores de paquetes de Dart probando la migración usando builds de preview temprana de null safety, y sus comentarios han sido alentadores. La guía de migración tiene consejos adicionales sobre cómo usar la herramienta de migración.

Paso 3: Analiza estáticamente tu código migrado

#

Actualiza tus paquetes usando pub get en tu IDE o en la línea de comandos. Luego usa tu IDE o la línea de comandos para realizar análisis estático en tu código Dart:

$ dart pub get
$ dart analyze

O en tu código Flutter:

$ flutter pub get
$ flutter analyze

Paso 4: Asegúrate de que las pruebas pasen

#

Ejecuta tus pruebas y asegúrate de que pasen. Puede que necesites actualizar pruebas que esperan valores null, en caso de que hayas cambiado el código de tu paquete para no permitir más nulls.

Paso 5: Publica tu paquete null-safe

#

Cuando la migración esté completa y las pruebas pasen, puedes publicar tu paquete como prerelease. Aquí hay un breve resumen de las buenas prácticas:

  • Incrementa tu número de versión a la siguiente versión mayor (por ejemplo, de 2.3.x a 3.0.0). Esta buena práctica asegura que los usuarios de tu paquete no se actualicen antes de estar listos para usar null safety ellos mismos, y te da la libertad de refactorizar tus APIs para aprovechar al máximo null safety.

  • Versiona y publica tu paquete como una versión prerelease en pub.dev. (Por ejemplo, usa 3.0.0-nullsafety.0, no 3.0.0.)

Para obtener todos los detalles sobre migración y versionado, consulta la guía de migración.

Los beneficios de sound null safety

#

Nuestras publicaciones anteriores de blog para las previews técnicas de null safety en Dart y en Flutter han discutido los beneficios de null safety mediante varios ejemplos. Ahora que null safety está cerca de completarse, estamos viendo varios ejemplos del mundo real de este beneficio.

Código más seguro

#

Hace poco, descubrimos un bug en el canal master de Flutter donde varios comandos de la herramienta flutter fallaban en ciertas configuraciones de máquina con un error de null: The method '>=' was called on null. El problema subyacente era un pull request reciente para añadir soporte para detectar Android Studio 4.1. Ese PR añadió código como este:

final int major = version?.major;
final int minor = version?.minor;
if (globals.platform.isMacOS) {
  /// plugin path of Android Studio changed after version 4.1.
  if (major >= 4 && minor >= 1) {
    ...

¿Puedes detectar el error? Como la versión puede ser null, tanto major como minor también pueden ser null. Este bug puede parecer fácil de detectar aquí de forma aislada, pero en la práctica código como este se cuela todo el tiempo, incluso con un proceso riguroso de revisión de código como el que se usa en el repositorio de Flutter. Con null safety, el análisis estático detecta este problema inmediatamente:

Una interfaz de editor mostrando un error de compilación porque una variable nullable se usa sin una verificación de null.

Ese fue un error bastante simple. Durante nuestro uso temprano de null safety en código internamente en Google, hemos visto errores mucho más complejos ser detectados y luego resueltos gracias a null safety. Aquí hay algunos ejemplos:

  • Un equipo interno descubrió que a menudo verificaban valores null en código que null safety sabía que nunca podría ser null. Este problema se veía con mayor frecuencia en código que usaba protobuf, donde los campos opcionales devuelven un valor por defecto cuando no están establecidos, y nunca null. Esto causaba que el código verificara incorrectamente la condición por defecto, confundiendo valores por defecto con valores null.

  • El equipo de Google Pay encontró bugs en su código de Flutter donde fallaban al intentar acceder a objetos State de Flutter fuera del contexto de un Widget. Antes de null safety, esos devolvían null y ocultaban el error; con null safety, el análisis sólido determinó que esas propiedades nunca podrían ser null, y lanzó un error de análisis.

  • El equipo de Flutter encontró un bug donde el motor de Flutter podía potencialmente fallar si se pasaba null al parámetro scene en Window.render(). Durante la migración a null safety, añadieron una pista para marcar Scene como no-nullable, y luego pudieron prevenir fácilmente potenciales caídas de la app que null habría provocado si se pasaba.

Aprovechar sound null safety durante la compilación

#

La solidez del null safety de Dart tiene otra implicación bienvenida: significa que los compiladores de Dart pueden aprovechar la información de nulabilidad. Esto potencialmente puede hacer tus programas más pequeños y rápidos. Todavía no tenemos muchas apps del mundo real migradas completamente a sound null safety (ya que estamos comenzando ahora la migración del ecosistema de paquetes del que estas apps dependen para la solidez), pero estamos viendo resultados muy alentadores del framework central.

Recientemente hicimos una recompilación de prueba del ejemplo hello_world para medir el impacto de null safety en el tamaño de la app. Este es un ejemplo mínimo que simplemente muestra "hello world". Al comparar el tamaño total del código compilado, el tamaño del código sin comprimir (instalado en el dispositivo) se redujo un 3.5% sin hacer nada más que recompilar con sound null safety. Esto fue posible, a pesar de que esta app es de apenas 10 líneas de código, porque el tamaño de código de todas las bibliotecas incluidas se redujo; por ejemplo, el framework de Flutter en sí (package:flutter) se redujo un 3.9%.

En cuanto a la velocidad del código, tener que hacer cumplir un sistema de tipos sólido potencialmente añade sobrecarga. Sin embargo, tener menos verificaciones de null también potencialmente hace el código más rápido. Nuestro análisis inicial de benchmarks muestra que el rendimiento está a la par con versiones anteriores, y que la nueva información de tipos adicional crea el potencial para que hagamos nuevos tipos de mejoras de rendimiento en el futuro. Planeamos escribir más sobre nuestro trabajo de rendimiento en futuras publicaciones de blog.

En algunos casos ya hemos visto que null safety conduce a mejoras de rendimiento, típicamente cuando la migración a null safety ha descubierto un defecto en la lógica del código. Por ejemplo, encontramos un problema en la caché de diseño de texto web de Flutter. Esta caché estaba usando una clave que era nullable, y luego algo de lógica para usar TextAlign.start cuando era null. Esta lógica causaba un defecto en la caché donde los elementos parecería que habían cambiado aunque todavía tuvieran un valor por defecto. Como resultado, había frecuentes fallos de caché. Añadir un getter no-nullable textAlign ayudó a arreglar el defecto de la caché, resultando en un aumento de 14x en el rendimiento de renderizado de texto en casos donde el texto está en caché.

¡Comienza hoy!

#

Las versiones beta de Dart y Flutter que contienen null safety están listas hoy. Si desarrollas con Flutter, puedes cambiar a beta con flutter channel beta seguido de flutter upgrade. De lo contrario, puedes obtener el SDK de Dart independiente desde el archivo del SDK de Dart.

Si desarrollas paquetes, te animamos a leer nuestra guía de migración y planificar tu migración. Por favor cuéntanos sobre cualquier problema o sugerencia que tengas.

Si eres desarrollador de apps, puede que prefieras retrasar la migración hasta que la función esté en nuestros canales estables. Planeamos abordar los comentarios de la beta rápidamente, arreglando cualquier problema restante. Es difícil establecer un cronograma concreto para cuándo null safety estará en una versión estable, pero estamos pensando a principios del próximo año.

¡Gracias por tu apoyo y comentarios mientras trabajamos para hacer de Dart un lenguaje más robusto, y de Flutter un framework más fuerte!

Más de Dart