66 puntos por GN⁺ 2025-08-18 | 1 comentarios | Compartir por WhatsApp
  • Trampas poco intuitivas en las que suelen caer los desarrolladores: se presenta un resumen con las causas de bugs que aparecen con facilidad
  • Se abordan problemas frecuentes en distintas tecnologías como HTML, CSS, Unicode/codificación de texto, punto flotante y tiempo
  • Se enfatiza que las diferencias sutiles de sintaxis y comportamiento entre lenguajes y frameworks pueden causar malentendidos o errores
  • Se explican con ejemplos trampas que pueden aparecer en entornos reales de producción en áreas clave del backend como concurrencia, redes y bases de datos
  • A través de diversos ejemplos y enlaces de referencia, se muestran los problemas, las soluciones y mejoras para comportamientos inesperados

HTML y CSS

  • Valor predeterminado de min-width en Flexbox/Grid

    • min-width es auto por defecto
    • min-width: auto se determina por el tamaño del contenido y tiene prioridad sobre flex-shrink, overflow: hidden, width: 0, max-width: 100%
    • Recomendación: especificar min-width: 0
  • Diferencia entre horizontal y vertical en CSS

    • width: auto intenta llenar el espacio del padre, mientras que height: auto se ajusta al contenido
    • En elementos inline, inline-block y float, width: auto no se expande
    • margin: 0 auto centra horizontalmente; margin: auto 0 no permite centrar verticalmente (aunque con flex-direction: column sí es posible centrar verticalmente)
    • La fusión de márgenes solo ocurre en vertical
    • Si cambia la dirección del layout, como con writing-mode: vertical-rl, el comportamiento también se invierte
  • Block Formatting Context (BFC)

    • Se puede crear un BFC con display: flow-root (también con overflow: hidden/auto/scroll, display: table, etc., aunque con efectos secundarios)
    • Un BFC puede evitar que se superpongan márgenes verticales entre hermanos adyacentes o que el margen de un hijo se “escape” fuera del padre
    • Si un padre contiene solo hijos con float, su altura colapsa a 0 → se puede corregir con un BFC
    • Si hay border o padding, no ocurre la fusión de márgenes
  • Stacking Context

    • Condiciones que crean un nuevo stacking context
      • Propiedades de renderizado como transform, filter, perspective, mask, opacity, etc.
      • position: fixed o sticky
      • z-index especificado + posicionamiento absolute/relative
      • z-index especificado + elemento dentro de flexbox/grid
      • isolation: isolate
    • Características
      • z-index solo se aplica dentro del stacking context
      • Las coordenadas de position: absolute/fixed se basan en el ancestro posicionado más cercano
      • sticky no funciona atravesando un stacking context
      • Incluso overflow: visible se recorta por efecto del stacking context
      • background-attachment: fixed se posiciona con base en el stacking context
  • Unidades del viewport

    • En navegadores móviles, si la barra de direcciones o la barra de navegación desaparece al hacer scroll, el valor de 100vh cambia
    • Solución moderna: usar 100dvh
  • Referencia de Absolute Position

    • position: absolute no toma como referencia al padre, sino al ancestro relative/absolute o stacking context más cercano
  • Comportamiento de Blur

    • backdrop-filter: blur no toma en cuenta los elementos alrededor
  • Invalidación de float

    • Si el padre es flex o grid, el float de los hijos no tiene efecto
  • width/height en porcentaje

    • No funcionan si el tamaño del padre no está determinado de antemano (para evitar referencias circulares)
  • Características de los elementos inline

    • display: inline ignora width, height, margin-top y margin-bottom
  • Manejo de whitespace

    • Por defecto, los saltos de línea en HTML se tratan como espacios y los espacios consecutivos se reducen a uno solo
    • `` evita la reducción de espacios, pero tiene un comportamiento particular al inicio y al final
    • En la mayoría de los casos, los espacios al inicio o al final del contenido se ignoran, pero `` es una excepción
    • Los espacios o saltos de línea entre inline-block se muestran como separación real (no ocurre en flex/grid)
  • text-align

    • Se aplica a la alineación de texto y elementos inline, pero no a la alineación de elementos block
  • box-sizing

    • El valor predeterminado es content-box → no incluye padding/border
    • Si se usa width: 100% junto con padding, puede desbordar el área del padre
    • Solución: box-sizing: border-box
  • Cumulative Layout Shift

    • Si no se especifican los atributos width y height en ``, la carga tardía de la imagen puede provocar saltos de layout
    • Recomendación: especificar los atributos para evitar CLS
  • Solicitudes de red de descargas de archivos en Chrome

    • No aparecen en el panel Network de DevTools (se procesan en otra pestaña)
    • Si hace falta analizarlas, usar chrome://net-export/
  • Problemas de parsing de JavaScript dentro de HTML

    • En casos como console.log(''), el primer `` se interpreta como etiqueta de cierre
    • Referencia: Safe JSON in script tags

Unicode y codificación de texto

  • Code points y grapheme clusters

    • Un grapheme cluster es la “unidad de carácter” en la GUI
    • En caracteres ASCII visibles, 1 code point = 1 grapheme cluster
    • Un emoji puede ser un solo grapheme cluster compuesto por varios code points
    • En UTF-8, un code point ocupa de 1 a 4 bytes; la cantidad de bytes no coincide con la cantidad de code points
    • En UTF-16, un code point ocupa 2 o 4 bytes (surrogate pair)
    • El estándar no limita la cantidad de code points dentro de un cluster, pero las implementaciones sí suelen imponer límites por rendimiento
  • Diferencias en el comportamiento de strings según el lenguaje

    • Rust: internamente usa UTF-8 para strings, len() devuelve la cantidad de bytes, no permite indexación directa, chars().count() devuelve la cantidad de code points, valida estrictamente la validez de UTF-8
    • Golang: un string es en la práctica un arreglo de bytes, la longitud y la indexación son por bytes, normalmente usa UTF-8
    • Java, C#, JS: basados en UTF-16, miden la longitud en unidades de 2 bytes y también indexan en unidades de 2 bytes; existen surrogate pairs
    • Python: len() devuelve la cantidad de code points, y la indexación devuelve un string que contiene un code point
    • C++: std::string no impone restricciones de codificación, funciona como un vector de bytes y la longitud/indexación son por bytes
    • Entre los lenguajes mencionados, ninguno mide la longitud ni indexa por grapheme clusters
  • BOM (Byte Order Mark)

    • Algunos archivos de texto tienen BOM; por ejemplo, EF BB BF indica codificación UTF-8
    • Se usa principalmente en Windows, y el software no Windows puede no procesarlo correctamente
  • Otras consideraciones

    • Al convertir datos binarios a cadena, las partes inválidas se reemplazan por � (U+FFFD)
    • Existen confusable characters (caracteres que se ven parecidos entre sí)
    • Normalización (Normalization): por ejemplo, é puede representarse como U+00E9 (un solo code point) o U+0065+U+0301 (dos code points)
    • Existen zero-width characters e invisible characters
    • Diferencias de salto de línea: Windows usa CRLF \r\n, Linux/MacOS usa LF \n
    • Han unification: caracteres con formas ligeramente distintas según el idioma usan el mismo code point
      • La fuente renderiza correctamente incluyendo variantes por idioma
      • En internacionalización, es necesario elegir la variante de fuente correcta

Punto flotante (Floating point)

  • Propiedades de NaN

    • NaN no es igual a ningún valor, incluyendo a sí mismo (NaN == NaN siempre es false)
    • NaN != NaN siempre es true
    • El resultado de operaciones que incluyen NaN, en la mayoría de los casos, se propaga como NaN
  • Valores especiales

    • Existen +Inf y -Inf, y son distintos de NaN
    • -0.0 es un valor distinto de +0.0
      • En comparaciones son iguales, pero en algunos cálculos se comportan distinto
      • Ejemplo: 1.0 / +0.0 == +Inf, 1.0 / -0.0 == -Inf
  • Compatibilidad con JSON

    • El estándar JSON no permite NaN ni Inf
      • En JS, JSON.stringify convierte NaN e Inf a null
      • En Python, json.dumps(...) imprime NaN e Infinity tal cual (violando el estándar)
        • Si se usa la opción allow_nan=False, se produce ValueError cuando hay NaN/Inf
      • En Golang, json.Marshal devuelve error si existen NaN/Inf
  • Problemas de precisión

    • La comparación directa de punto flotante puede fallar → se recomienda una forma como abs(a - b) < ε
    • JS trata todos los números como punto flotante
      • El rango seguro de enteros es -(2^53 - 1) ~ 2^53 - 1
      • Fuera de ese rango, la representación de enteros es imprecisa
      • Se recomienda usar BigInt para enteros grandes
      • Si JSON incluye enteros fuera del rango seguro, el valor resultante de JSON.parse puede ser impreciso
      • Los timestamps en milisegundos son seguros hasta el año 287,396; en nanosegundos aparecen problemas
  • No aplicación de leyes algebraicas

    • Según el orden de las operaciones, por pérdida de precisión, la asociatividad y la distributividad no se cumplen estrictamente
    • Las operaciones paralelas (multiplicación de matrices, sumas, etc.) pueden producir resultados no deterministas
  • Rendimiento

    • La división es mucho más lenta que la multiplicación
    • Si se divide varias veces por el mismo número, se puede optimizar calculando primero su recíproco y multiplicando
  • Diferencias según el hardware

    • Compatibilidad con FMA (Fused Multiply-Add): algunos hardware hacen cálculos intermedios con mayor precisión
    • Manejo del subnormal range: el hardware moderno lo soporta, pero algunos equipos antiguos lo tratan como 0
    • Diferencias en los modos de redondeo
      • Existen RNTE (redondeo al par más cercano), RTZ (truncamiento hacia 0), etc.
      • En x86/ARM se pueden configurar como estado mutable local al hilo
      • En GPU, el modo de redondeo varía según la instrucción
    • Diferencias en el comportamiento de funciones matemáticas como trigonométricas y logaritmos
    • x86 tiene FPU legacy de 80 bits y per-core rounding mode → no se recomienda usarlo
    • Además de esto, varios factores pueden hacer que los resultados de punto flotante difieran según el hardware
  • Métodos para mejorar la precisión

    • Construir un grafo de cálculo poco profundo (reducir cadenas continuas de multiplicaciones)
    • Evitar casos donde los valores intermedios sean extremadamente grandes o pequeños
    • Aprovechar operaciones de hardware como FMA

Tiempo (Time)

  • Segundo intercalar (Leap second)

    • El timestamp Unix ignora los segundos intercalares
    • Cuando ocurre un segundo intercalar, el tiempo puede alargarse o acortarse en el tramo cercano (Leap smear)
  • Zona horaria (Time zone)

    • UTC y el timestamp Unix son comunes en todo el mundo
    • La hora legible para personas depende de la zona horaria local
    • Se recomienda guardar timestamps en la DB y convertirlos en la UI
  • Horario de verano (DST)

    • En algunas regiones el reloj se ajusta una hora durante el verano
  • Sincronización NTP

    • Durante la sincronización puede ocurrir que el tiempo "retroceda"
  • Configuración de zona horaria del servidor

    • Se recomienda configurar el servidor en UTC
    • En sistemas distribuidos, tener zonas horarias distintas por nodo puede causar problemas
    • Después de cambiar la zona horaria del sistema, es necesario reconfigurar o reiniciar la DB
  • Reloj de hardware vs reloj del sistema

    • El reloj de hardware no tiene concepto de zona horaria
    • Linux: trata el reloj de hardware como UTC
    • Windows: trata el reloj de hardware como hora local

Java

  • == compara referencias de objetos; para comparar el contenido del objeto hay que usar .equals
  • Si no se sobrescriben equals y hashcode, en map/set la identidad del objeto se evalúa por referencia
  • Si se modifica el contenido de un objeto usado como key de un map o como elemento de un set, el comportamiento del contenedor se rompe
  • Un método que devuelve List puede retornar, según el caso, un ArrayList mutable o un Collections.emptyList() inmutable; si se modifica este último, ocurre UnsupportedOperationException
  • Existen métodos que devuelven Optional pero retornan null (no recomendado)
  • Si se hace return en un bloque finally, una excepción ocurrida en try o catch se ignora y se aplica el valor de retorno de finally
  • Existen librerías que ignoran interrupt; el proceso de inicialización de clases, incluyendo IO, puede romperse por un interrupt
  • En un thread pool, las excepciones de una tarea enviada con .submit() no se imprimen en el log por defecto y solo pueden verificarse mediante el future; si se ignora el future, no hay forma de ver la excepción
    • Las tareas de scheduleAtFixedRate se detienen silenciosamente si ocurre una excepción
  • Si un literal numérico empieza con 0, se trata como octal (0123 → 83)
  • El debugger llama a .toString() de las variables locales; algunas clases tienen efectos secundarios en toString(), así que el comportamiento del código puede cambiar durante la depuración (se puede desactivar en el IDE)

Golang

  • append() reutiliza memoria cuando hay capacidad disponible; al hacer append sobre un subslice, incluso puede sobrescribir la memoria padre
  • defer se ejecuta al retornar de la función, no al salir del scope del bloque
  • defer captura variables mutables
  • Sobre nil
    • Un nil slice y un empty slice son distintos
    • Un string no puede ser nil; solo existe la cadena vacía
    • Un nil map permite lectura pero no escritura
    • Comportamiento particular de interface nil: si el data pointer es null pero la type info no es null, no es igual a nil
  • Dead wait: existen casos reales de bugs de concurrencia en Go
  • Hay varios tipos de timeout, tratados en detalle en net/http

C/C++

  • Si guardas un puntero a un elemento de std::vector y luego el vector crece, puede haber una reasignación y el puntero queda invalidado
  • Un std::string creado a partir de una cadena literal puede ser un objeto temporal; llamar a c_str() puede ser riesgoso
  • Si modificas un contenedor mientras iteras sobre él, se invalidan los iteradores
  • std::remove no elimina realmente; solo reordena los elementos, para borrar hace falta erase
  • Si un literal numérico empieza con 0, se interpreta como octal (0123 → 83)
  • Undefined behavior (UB): durante la optimización, el UB puede transformarse libremente, así que es peligroso depender de él
    • Acceder a memoria no inicializada es UB
    • Convertir char* a un puntero a struct y acceder antes de que comience la vida del objeto es UB; se recomienda inicializar con memcpy
    • El acceso inválido a memoria (como un puntero null) es UB
    • El overflow/underflow de enteros es UB (en unsigned puede haber underflow por debajo de 0)
    • Aliasing: si punteros de tipos distintos referencian la misma memoria, puede producirse UB por la strict aliasing rule
      • Excepciones: 1) tipos con relación de herencia 2) conversiones a char*, unsigned char*, std::byte* (la conversión inversa no aplica)
      • Para conversiones forzadas se recomienda memcpy o std::bit_cast
    • El acceso a memoria desalineada es UB
  • Alineación de memoria
    • Los enteros de 64 bits deben estar en direcciones divisibles entre 8
    • En ARM, el acceso desalineado puede provocar un crash
    • Interpretar directamente un buffer de bytes como una struct puede causar problemas de alineación
    • La alineación puede introducir padding en una struct y desperdiciar memoria
    • Algunas instrucciones SIMD (como AVX) solo pueden procesar datos alineados; normalmente requieren alineación de 32 bytes

Python

  • Los argumentos por defecto de una función no se crean de nuevo en cada llamada; se conserva el valor inicial

SQL Databases

  • Manejo de Null

    • x = null no funciona; hay que usar x is null
    • Null no es igual ni a sí mismo (similar a NaN)
    • Un unique index permite valores Null duplicados (excepto en Microsoft SQL Server)
    • En select distinct, la forma de tratar Null varía según la base de datos
    • count(x) y count(distinct x) ignoran las filas con valores Null
  • Comportamiento general

    • La conversión implícita de fechas puede depender de la timezone
    • Un join complejo + distinct puede ser más lento que una consulta anidada
    • En MySQL(InnoDB), si un campo string no es utf8mb4, insertar caracteres UTF-8 de 4 bytes produce error
    • MySQL(InnoDB) por defecto no distingue mayúsculas de minúsculas
    • MySQL(InnoDB) permite conversión implícita: select '123abc' + 1; → 124
    • El gap lock de MySQL(InnoDB) puede provocar deadlock
    • En MySQL(InnoDB), si group by y las columnas de select no coinciden, puede devolver resultados no deterministas
    • En SQLite, si no está en modo strict, el tipo del campo importa poco
    • Las foreign keys pueden generar locks implícitos y provocar deadlocks
    • El locking puede romper repeatable read isolation según la base de datos
    • Las bases de datos SQL distribuidas pueden no soportar locking o tener comportamientos particulares (depende de cada DB)
  • Rendimiento/operación

    • El problema de N+1 query no aparece en el slow query log porque cada consulta individual es rápida
    • Las transacciones de larga duración pueden causar problemas de locks, etc. → se recomienda terminarlas rápido
    • Casos de lock de tabla completa
      • En MySQL 8.0+, al agregar un unique index/foreign key normalmente se puede procesar de forma concurrente
      • En versiones antiguas de MySQL puede ocurrir un lock de tabla completa
      • Si mysqldump no lleva la opción --single-transaction, hace un read lock de toda la tabla
      • En PostgreSQL, create unique index o alter table ... add foreign key provocan un read lock de toda la tabla
        • Cómo evitarlo: usar create unique index concurrently
        • Para foreign key: usar ... not valid y luego validate constraint
  • Consultas por rango

    • Rangos no superpuestos:
      • La condición simple p >= start and p file 2>&1 → stdout+stderr ambos al archivo, cmd 2>&1 > file → solo stdout al archivo, stderr queda igual
  • Los nombres de archivo distinguen mayúsculas y minúsculas (a diferencia de Windows)

  • Los ejecutables cuentan con un sistema de capabilities (se puede verificar con getcap)

  • Riesgo de variables unset: si DIR no está definido, rm -rf $DIR/ puede terminar ejecutando rm -rf / → se puede prevenir con set -u

  • Aplicar el entorno: para aplicar un script al shell actual, usar source script.sh → para aplicarlo de forma permanente, agregarlo a ~/.bashrc

  • Bash hace caché de comandos: si se mueve un archivo dentro de $PATH, puede aparecer ENOENT → actualizar la caché con hash -r

  • Si se usa una variable sin comillas, los saltos de línea se tratan como espacios

  • set -e: termina el script de inmediato ante errores, pero no funciona dentro de condicionales (||, &&, if)

  • Conflicto entre K8s livenessProbe y el depurador: un depurador con breakpoints puede detener toda la app y hacer que falle la respuesta al health check → el Pod puede terminarse

React

  • Modificar state directamente dentro del código de renderizado
  • Usar Hooks dentro de if/loop → viola las reglas
  • Omitir valores necesarios en el dependency array de useEffect
  • Omitir código de limpieza (clean up) en useEffect
  • Trampa de closure: se captura un state antiguo y eso provoca bugs
  • Modificar datos en el lugar incorrecto → componente impuro
  • Omitir useCallback → provoca rerenderizados innecesarios
  • Pasar valores no memorizados a componentes memorizados invalida la optimización de memo

Git

  • Rebase reescribe el historial

    • Después de un rebase, un push normal genera conflicto → es necesario hacer force push
    • Si cambia el historial de la rama remota, al hacer pull también se debe usar --rebase
    • --force-with-lease puede evitar en algunos casos sobrescribir commits de otros desarrolladores, pero si solo se hace fetch y no pull, no brinda esa protección
  • Problema con revert de merge

    • Hacer revert de un merge no revierte completamente sus efectos → al volver a hacer merge de la misma rama no habrá cambios
    • Solución: hacer revert del revert o usar un método más limpio (backup → reset → cherry-pick → force push)
  • Precauciones relacionadas con GitHub

    • Aunque se sobrescriba con force push un commit que contiene secretos como API keys, GitHub conserva el registro
    • Si B es un fork privado de un repo privado A, y A se vuelve público, el contenido de B también queda expuesto (puede seguir siendo accesible incluso después de eliminarlo)
  • git stash pop: si ocurre un conflicto, el stash no se elimina

  • .DS_Store lo genera macOS automáticamente → se recomienda agregar **/.DS_Store a .gitignore

Networking

  • Algunos routers y firewalls cierran silenciosamente conexiones TCP inactivas → pueden invalidar los pools de conexiones de clientes HTTP o DB → solución: configurar TCP keepalive
  • Los resultados de traceroute tienen baja confiabilidad → en algunos casos tcptraceroute puede ser más útil
  • TCP slow start puede aumentar la latencia → se puede resolver desactivando tcp_slow_start_after_idle
  • Problema de sticky packets en TCP: el algoritmo de Nagle retrasa el envío de paquetes → se puede resolver activando TCP_NODELAY
  • Al colocar un backend detrás de Nginx, hay que configurar la reutilización de conexiones → si no se configura, en alta carga pueden fallar conexiones por falta de puertos internos
  • Nginx hace buffering de paquetes por defecto → puede provocar retrasos en SSE (EventSource)
  • El estándar HTTP no prohíbe body en solicitudes GET o DELETE → algunos lo usan, pero muchas librerías y servidores no lo soportan
  • Se pueden alojar varios sitios web en una sola IP → la distinción la hacen el header HTTP Host y el SNI de TLS → hay sitios a los que no se puede acceder solo con la IP
  • CORS: cuando una solicitud se hace a otro origin, el navegador bloquea el acceso a la respuesta → el servidor debe configurar el header Access-Control-Allow-Origin
    • Si también se envían cookies, se requiere configuración adicional
    • Si frontend y backend usan el mismo dominio y puerto, no hay problema de CORS

Other

  • Precauciones con YAML

    • YAML es sensible a los espacioskey:value es un error, key: value es lo correcto
    • Si el código de país NO se escribe sin comillas, puede interpretarse como false
    • Si un hash de commit de Git se escribe sin comillas, puede convertirse en número
  • Problemas de Excel con CSV

    • Al abrir un CSV, Excel hace conversiones automáticas
      • Conversión de fechas: 1/2, 1-22-Jan
      • Conversión inexacta de números grandes: 1234567890123456789012345678901234500000
    • La causa es que Excel maneja internamente los números como floating point
    • Existe un caso en el que el nombre de un gen, SEPT1, fue modificado incorrectamente por este problema

1 comentarios

 
GN⁺ 2025-08-18
Opinión de Hacker News
  • Algunos routers y firewalls pueden cerrar silenciosamente conexiones TCP inactivas sin enviar ninguna señal a la aplicación; el código que mantiene pools de conexiones TCP, como las librerías cliente HTTP o los clientes de bases de datos, puede sufrir que una conexión quede inválida sin ninguna advertencia. Para resolverlo, se puede configurar TCP keepalive del sistema o, en HTTP, usar los headers Connection: keep-alive, Keep-Alive: timeout=30, max=1000. Una vez establecida una conexión TCP, los routers intermedios no conservan estado; el problema son los timeouts de sesión del firewall o del NAT. En ese caso tampoco llega un paquete RST. En entornos K8s, hubo casos donde la configuración del módulo conntrack estaba demasiado baja. Incluso si se usa HTTP Keep-Alive, eso solo ayuda a reutilizar la conexión y no a mantener viva la conexión en la red (enlace explicativo); HTTP Keep-Alive en realidad no genera paquetes, solo retrasa el cierre. En cambio, TCP Keep-Alive sí genera paquetes de forma periódica para reiniciar el temporizador

    • TCP Keep-Alive puede no funcionar bien con dispositivos móviles; los sistemas operativos móviles solo pueden rastrear o gestionar individualmente los paquetes keep-alive que se generan a nivel de aplicación. Pero como TCP Keep-Alive opera por debajo del nivel de la aplicación, a veces puede desactivarse, y aun así la app seguir siendo accesible
  • Un método que devuelve Optional<T> puede devolver null; esa práctica es demasiado confusa. Si tuviera suficiente serenidad emocional, habría propuesto un JEP para introducir una anotación como @java.lang.NonNullReference. Querría que, al declarar un tipo con esa anotación, el compilador tratara como error cualquier asignación de null; por ejemplo, Alpha podría aceptar asignación de null, pero Beta daría error. También parece que habría que revisar la especificación para entender cómo funciona realmente la eliminación de código muerto en javac; por ejemplo, en if (true) la parte donde b = null en la práctica se omite, y legalmente podría ser código permitido

    • En Kotlin eso ya da error de compilación, sin necesidad de ninguna anotación

    • Me pregunto si realmente hace falta usar Optional<T> en un lenguaje con null; como en Python, donde el valor de retorno de una función se expresa simplemente como T | None y no como un objeto Optional, si al final hay que hacer la misma verificación, no queda tan clara la diferencia del framework. A menos que uses un estilo monádico particular, la comprobación termina siendo la misma

  • Se dijo que Java, C# y JS codifican los strings en memoria como algo tipo UTF-16, pero en Java eso no es correcto; en C# y JS podría pasar lo mismo. Si el tipo string de un lenguaje es lo bastante opaco, la representación en memoria es un detalle de implementación. En Java eso quedó claro desde la versión 9 (JEP relacionado). Esa también es una de las razones por las que, cuando existe FFI, es difícil cambiar detalles de implementación. Además, sobre los números en JS se dice que el entero máximo representable con exactitud es 2^53−1, pero en realidad enteros mayores como 2^100 también pueden representarse exactamente. 2^53−1 significa que n-1, n y n+1 se representan todos con exactitud en un double IEEE; por eso n == n-1 y n == n+1 dan ambos false

    • En C# la representación en memoria está muy fijada; como es común acceder directamente al buffer con ReadOnlySpan<char> o raw char*, char es un tipo de punto de código UTF-16. Tal vez en JS se pueda esquivar de alguna manera

    • Preferiría decir max safe integer en vez de max accurate integer

    • (Sobre codificación base64) Respecto a que los strings en memoria de Java, C# y JS no sean de tipo UTF-16, puede que técnicamente sea cierto, pero sí hay casos donde, si codificas en base64 un string en un lenguaje basado en UTF-8 y luego lo decodificas en Java, surgen problemas por la representación UTF-16 de Java

  • Parece que los tips o la información en formato de manual solo se entienden más rápido cuando ya los conocías o casi los conocías. La mayoría de los manuales sirven más para organizar y repasar que para aprender desde cero, y son poco eficientes para enseñar a personas que no saben nada del tema

    • Un manual, por naturaleza, existe para dejar registro y no depender solo de la memoria. La mayoría de los manuales de Unix tienen esa forma. Uno busca el manual cuando ya sabe qué hace cierto software, pero olvidó los detalles concretos de uso. En cambio, para que alguien completamente principiante aprenda conceptos, hace falta un tutorial o una guía. El manual funciona más como preparación para poder hacer mejores preguntas
  • Leí “Traceroute Isn’t Real” con muchísimo interés. Ya venía sintiendo que los datos de traceroute eran muy inexactos o parecían carecer de sentido, y fue útil entender por qué (enlace original). Si alguien tiene información más actual, estaría bueno conocerla

  • Este texto no es tanto una lista de trampas reales como una recopilación de pequeños tips que su autor aprendió por experiencia. Muchas cosas solo aplican en contextos muy específicos, pero ese contexto no queda claro, y algunas incluso parecen incorrectas. Así que no hace falta tomar todo el texto al pie de la letra; conviene verlo más como una especie de flujo de ideas o notas

  • Lo de que en Python los valores por defecto de los argumentos no se crean de nuevo en cada llamada, sino que son valores almacenados, es algo que definitivamente hay que saber al usar variables datetime

    • No era un desarrollador que usara Python todo el tiempo, pero esta semana sufrí bastante por el hecho de que los valores por defecto de los argumentos se guardan. Quería asignar un set vacío cuando no se pasara uno como argumento, pero el set se reutilizaba y eso provocó un bug. Me tomó bastante tiempo entender la causa
  • En la primera “trampa” de la página se decía que min-width: auto determina el ancho mínimo según el contenido, pero eso no es cierto fuera de flex/grid. Según MDN, en block, inline, table, etc., auto se ajusta a 0 (documentación oficial)

    • La verdadera primera trampa es que “no se puede leer ninguna propiedad de CSS de forma aislada”. Tal como indica el propio nombre cascading, los valores por defecto y el resultado de múltiples reglas terminan combinándose en algún punto, así que el contexto de todo el documento importa

    • La cascade de las propiedades de texto en CSS todavía se puede entender. Pero el layout de CSS es demasiado difícil de entender desde cualquier perspectiva: diseñador de la página, implementador o usuario. No logro empatizar con para quién está hecho ese diseño

  • En general es una buena lista. Tengo algunas observaciones.

    • Sobre la unificación de Unicode: que caracteres con el mismo significado en varios idiomas usen el mismo code point y se muestren distinto según la fuente no es una trampa. Los hanzi del ejemplo se usan casi igual tanto en chino como en japonés, y los usuarios de ambos idiomas reconocen también otras variantes como el mismo carácter conceptual. El autor está planteándolo como si hubiera que definir la letra “A” por separado en inglés y en francés, pero en realidad no es así. Ver la entrada de Han unification
    • -0.0 y +0.0 (cero negativo y cero positivo) se consideran iguales en comparaciones de punto flotante, pero hay formas de distinguirlos; por ejemplo, por su patrón de bits o por el resultado de 1.0/-0.0 = -infinito y 1.0/0.0 = +infinito
    • Coincido mucho con la recomendación de usar UTC como zona horaria del servidor; usar UTC en servidores, logs, almacenamiento de fotos y cualquier lugar donde importe preservar el valor o se necesiten timestamps precisos. La hora local debería usarse solo para la conversación
    • Se dijo que en enteros conviene usar (low + high) / 2 por riesgo de overflow y reemplazarlo por low + (high - low) / 2, pero si low o high pueden ser negativos, eso podría simplemente mover el rango del overflow. Es un tema importante en la búsqueda binaria común
    • También es una gran trampa usar correctamente tipos y operaciones enteras en C/C++; guía relacionada
    • Se dijo que rebase puede cambiar el history, pero rebase es, por definición, un command que reescribe el history
    • Aunque los usuarios de ambos idiomas reconozcan conceptualmente el mismo carácter, no hay que descartarlo como una simple “variante tipográfica”. Que el code point de Unicode sea el mismo no significa que el reemplazo del carácter sea seguro; desde la perspectiva de un usuario japonés, ese tipo de sustitución puede incluso ser motivo para rechazar el uso de un producto

    • En realidad no se distingue entre la A inglesa y la A francesa, pero sí existen casos como А (cirílica) y A (latina), que se ven igual aunque tengan code points distintos. Además, la unificación Han a menudo agrupa como uno solo caracteres con formas bastante diferentes, así que quienes estudian japonés o chino realmente pueden confundirse. Por ejemplo, el carácter '喝' (drink) puede verse bastante distinto según el caso, como se muestra en este enlace; incluso al copiarlo, la forma puede cambiar de inmediato, así de complejo es el tratamiento. La unificación Han es, en la práctica, un tema bastante complicado

  • Se mencionó que hay diferencias sutiles entre numpy y pytorch, pero sin explicar casos concretos no resulta muy útil y la información queda demasiado incompleta como para sentirlo como una trampa real