Ingeniero Móvil Sénior · iOS, macOS y visionOS nativos · Flutter y las APIs detrás

Maykon Meneghel

Entrego la app Apple nativa y la API con la que habla.

La mayoría de los CV te cuentan lo que alguien sabe hacer. Este te deja probarlo. Debajo de todo esto hay una sola cosa: una lectura. Construí el instrumento que produce una — firmware, placa, carcasa, enterrado en un campo — y pasé los siete años siguientes en todo lo que le ocurre a un número después. Cada capítulo de abajo es un mini-juego que funciona.

01 · THREAD iOS y Flutter · 2019 → hoy

El frame dura dieciséis milisegundos

Toda discusión de concurrencia en un equipo móvil es, en el fondo, sobre un solo número. Una pantalla debe un frame cada 16,7 ms, y todo — async/await, actors, isolates, cancelación — trata de lo que puedes hacer dentro de él. Abajo está la misma lista cargada de tres formas, con el gráfico de frames que te mostraría un profiler.

instruments · frame timing

Una pantalla debe un frame cada 16,7 ms. Esta está cargando veinticuatro filas, y lo único que cambia entre los tres botones es dónde ocurre el decode. Mira el gráfico de frames, no la lista.

Dónde corre el trabajo
iPhone · 60 Hz
Tiempos de frame presupuesto de 16,7 ms

dentro perdido

Peor frame
Frames perdidos
Jank
Desperdiciado al salir

Un frame que dura casi trescientos milisegundos. La lista no tartamudea — se detiene, y el sistema operativo empieza a plantearse si tu app sigue viva. Todo ingeniero móvil ha publicado esto una vez.

Swift
 
Flutter
 

Lo que acabas de aprender → El botón del medio es el capítulo entero. await suspende una función, no la mueve — la continuación se reanuda en el main actor y el trabajo caro sigue cayendo dentro de un frame. Código asíncrono que nunca sale del hilo principal es el bug de rendimiento más común en móvil, y es invisible hasta que alguien lo perfila.

Swift Concurrencyasync/awaitactors@MainActorCombineIsolatescompute()InstrumentsCancelación de Task60 fps

Acto uno

La pantalla

Una lectura no vale nada hasta que alguien la ve, y un teléfono da 16,7 milisegundos para mostrarla. Siete años entregando apps, nativas y multiplataforma — y antes de ellas tres años un nivel más abajo, en firmware, placas y piezas mecanizadas, que la Apple Developer Academy convirtió en esto: la misma ingeniería, movida hacia arriba en la pila. Este acto son los últimos centímetros de ese trayecto — llegar a tiempo, seguir legible en condiciones que nadie diseñó, y alcanzar solo a la persona correcta.

02 · CRISTAL Swift y Flutter · 2019 → hoy

La tarjeta que sobrevive al mundo real

Cualquiera construye la tarjeta del diseño. El trabajo es construir la que sigue siendo legible cuando la persona que sostiene el móvil puso el texto en el mayor tamaño de accesibilidad, está en vista dividida, en un idioma cuyas palabras son más largas, leyendo de derecha a izquierda, en una conexión que nunca entregó la imagen. El diseñador te da un estado. Tú entregas seis.

PositionRow.swift / position_row.dart

Una tarjeta, dos builds. A la izquierda la tarjeta tal como sale del diseño: una fila, cada texto en una línea, una altura en puntos. A la derecha la misma tarjeta construida para sobrevivir. Enciende las condiciones y mira cuál sigue siendo legible — el tamaño de letra que la persona realmente eligió, una vista dividida estrecha, una traducción más larga, un idioma de derecha a izquierda, una imagen que nunca llegó, un error sin sitio donde aparecer.

Ancho disponible
Condiciones
Estado de la vista
Hecho al diseño
BTC-USD Long · 20 lots +1,240.50
    Hecho para sobrevivir
    BTC-USD Long · 20 lots +1,240.50
      SwiftUI
      // The whole fix is three decisions.
      HStack(alignment: .firstTextBaseline, spacing: 12) {
        if let image { Thumbnail(image) }        // no image, no box
        VStack(alignment: .leading, spacing: 2) {
          Text(position.symbol)
            .font(.headline)
            .lineLimit(nil)                      // let it wrap
          if let error { Text(error).foregroundStyle(.red) }
          else { Text(position.summary).font(.subheadline) }
        }
        Spacer(minLength: 12)
        Text(position.pnl, format: .currency(code: code))
          .monospacedDigit()
      }
      .padding(.horizontal, 16).padding(.vertical, 12)
      .frame(minHeight: 44)                      // grows, never clips
      .dynamicTypeSize(...DynamicTypeSize.accessibility5)
      .accessibilityElement(children: .combine)
      .accessibilityLabel(position.spokenLabel)
      Flutter
      // Same three decisions, same order.
      Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          if (image != null) Thumbnail(image!),
          const SizedBox(width: 12),
          Expanded(                              // min-width: 0, in Flutter
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(p.symbol, style: t.titleMedium, softWrap: true),
                error != null
                  ? Text(error!, style: t.bodySmall!.copyWith(color: cs.error))
                  : Text(p.summary, style: t.bodySmall),
              ],
            ),
          ),
          const SizedBox(width: 12),
          Text(p.pnl, style: t.titleMedium),
        ],
      )
      // Directionality mirrors the row; Semantics gives it one spoken label.

      Lo que acabas de aprender → El layout dibujado al comp sobrevive a una de las siete condiciones de abajo — justo aquella para la que fue dibujado. La diferencia no es gusto, es qué pregunta hace el layout: no "¿la letra es grande?" sino "¿esto cabe?". Haz la segunda pregunta y una traducción más larga deja de ser un reporte de bug.

      SwiftUIUIKitFlutterDynamic TypeVoiceOverLocalizaciónRTLLayout adaptativoAccesibilidadDesign systems

      03 · PUERTA Tradx · 2022 → hoy

      El login es por donde entran a una app

      Todo lo demás en una app móvil falla de forma visible. Esto falla en silencio: el login funciona, la persona entra, y el flujo estuvo abierto todo el tiempo. Rehice esta parte en Tradx después de leer nuestra propia implementación con atención — son las cincuenta líneas que preguntaría en cualquier entrevista sénior.

      oauth · manejo del redirect

      Autenticar a alguien por el navegador y devolver el resultado a la app son las cincuenta líneas más sensibles de un código móvil. Empieza por la versión que muestra todo tutorial y ciérrala interruptor a interruptor. El orden importa más de lo que te gustaría.

      El redirect
      Qué vuelve
      Protecciones
      1. The app opens the browser ASWebAuthenticationSession, not a web view — the session cookie has to be the real one
      2. The user signs in and the authorisation server redirects back
      3. The device routes the redirect to whichever app claims that address
      4. The app reads the callback off the URL, which is written to logs and history
      5. The app exchanges it for a session, over TLS, from the app itself
      6. Somebody presents it again the same value, a second time
      7. The session lands in the keychain and the user is in
      El redirect llega a
      Ataques que pasan
      • Scheme hijack any other app on the device can register tradx:// and receive the redirect instead
      • Token in the logs an access token on a query string is written to system logs, browser history and the referrer
      • Code replay a code that is accepted twice is an access token with extra steps
      • Code interception without PKCE the exchange proves nothing about who started the flow, so whoever holds the code can spend it
      • Forged callback without an unguessable state value the app accepts a sign-in the user never started

      Empieza por la versión del tutorial y ciérrala interruptor a interruptor. Mira hasta dónde llega el redirect.

      Lo que acabas de aprender → Cambiar el token por un código de un solo uso es la primera corrección que todos intentan, y por sí sola abre dos agujeros para cerrar uno. Un código necesita PKCE para que solo la app que inició el flujo pueda gastarlo, y un solo uso para que no se gaste dos veces. Media migración de seguridad es peor que ninguna, porque parece terminada.

      OAuth 2.0PKCEUniversal LinksASWebAuthenticationSessionKeychainApp LinksDeep linkingModelado de amenazas

      Acto dos

      Con qué habla la app

      Vuelve hacia atrás desde la pantalla y encuentras el trayecto que hizo el número para llegar. Tiene que llegar mientras todavía es verdad, ser encontrado entre cientos de miles de hermanos, aguantar que todos pregunten a la vez, y por fin merecer una decisión. Cuatro capítulos — y en el último, las cuatro decisiones son mías.

      04 · PULSO Tradx · 2022 → hoy

      El cliente no debería tener que preguntar

      Casi todo lo que una app muestra es la respuesta a una pregunta que hizo. Un precio en vivo no: cuando llega la respuesta la pregunta ya cambió, y volver a preguntar gasta una petición entera para oír, casi siempre, que no pasó nada. Este capítulo es la diferencia entre un endpoint que responde y un canal que te avisa.

      tradx · feed de precios

      Un minuto de un feed de precios, reproducido. Elige cómo se entera el cliente — preguntar con un temporizador, o que le avisen — y luego mira los dos números: lo que hace el mercado, y lo que el lector está mirando de verdad.

      Transporte
      El mercado
      En pantalla
      0.0s
      Peticiones
      Bytes por minuto
      Ticks vistos
      Desactualizada

      Una conexión, {bytes} en todo el minuto, y llegaron los {emitted} ticks. La pantalla mostró un precio ya superado el {behind} del tiempo — el vuelo en sí, y nada más.

      Lo que acabas de aprender → Preguntar una vez por segundo costó ocho veces más bytes que mantener un socket abierto, y aun así se perdió un cuarto de los ticks. Frecuencia no es actualidad: la única forma de dejar de llegar tarde es dejar de preguntar.

      Node.jsNestJSWebSocketsAPIs RESTArquitectura orientada a eventosDiseño de API

      05 · SERVICIO Backend · 2018 → hoy

      Alguien tiene que recordarlo todo

      Una lectura que nadie guardó es una lectura que no ocurrió. Detrás de cada dispositivo y de cada cliente hay una API y una base de datos, y la diferencia entre un producto rápido y uno lento suele ser una sola decisión tomada aquí. La tabla de abajo tiene doscientas cuarenta mil lecturas de sensor — del tipo que la sonda de la otra página pasó dos años produciendo.

      psql · meneghel_db

      Una tabla de lecturas de sensor, 240.000 filas. Elige una consulta, decide qué columnas llevan índice, y ejecútala. La franja de abajo es la tabla; fíjate en cuánta tiene que tocar la base de datos.

      Consulta
      SELECT * FROM readings WHERE reading_id = 128374
      CREATE INDEX ON

      índice · árbol B

      18 pages

      tabla · readings · 240.000 filas

      Filas leídas
      Tiempo de consulta
      vs recorrido completo
      Coste de escritura

      Lo que acabas de aprender → Sin índice, la base lee todas las filas para responderte. Con uno, salta directo al punto. Misma consulta, mismos datos, mismo servidor — mil veces más rápido.

      Node.jsExpressRestifyMongoDBPostgreSQLREST

      06 · ENJAMBRE Backend · 2018 → hoy

      Una sola máquina nunca alcanza

      Un lector es una consulta; diez mil a la vez es una arquitectura. El tráfico no llega con educación — llega de golpe, a las 3 de la mañana, el día del lanzamiento. Los contenedores y Kubernetes son cómo respondes a eso sin despertarte.

      kubectl · production

      El tráfico de este servicio se dispara cada pocos segundos, como en un día de lanzamiento. Añade réplicas y la latencia baja; añade demasiadas y pagas por máquinas ociosas. Después déjaselo al autoscaler y mira dónde sigue perdiendo.

      Entrada Réplicas × 60 rps

      listos 0 arrancando 0

      Estás escalando a mano. El pico llega estés listo o no.

      Entrada 0
      Saturación 0%
      Latencia p99 0 ms
      Peticiones perdidas 0
      Coste $0

      Lo que acabas de aprender → No hiciste el código más rápido — hiciste más copias de él. El escalado horizontal cambia dinero por latencia, y un autoscaler hace ese cambio por ti, solo.

      DockerKubernetesHPABalanceo de cargaCI/CD

      07 · FLUJO Tradx · 2022 → hoy

      Y entonces construí la herramienta

      Tradx es mi sandbox — el lugar donde tomo todas las decisiones y convivo con todas ellas. Un cliente de trading de escritorio escrito en Flutter, donde una estrategia es un grafo que conectas en vez de código que escribes, publicado en la Mac App Store y en la Microsoft Store, con unas quince mil descargas entre las dos. Lo construimos dos: Luiz Veloso y yo, por pull request, con revisiones que ninguno se salta.

      tradx · grafo de estrategia

      Esto es lo que hace Tradx, en miniatura. No escribes el bucle — cableas la lógica, y el motor la ejecuta sobre las velas. Mueve los parámetros y mira cómo cambian a la vez el grafo y las operaciones.

      El editor de estrategias de Tradx: un gráfico de velas de PETR4 sobre un grafo de nodos que conecta una media móvil y un RSI a dos entradas a mercado.
      Tradx · el editor de estrategias. Siete nodos, siete conexiones, y un backtest corriendo sobre PETR4 en el gráfico de quince minutos.

      Dos personas, pull requests, y revisiones que ninguno se salta.

      Datos de mercado Indicador RSI Cruce al alza Cruce a la baja Entrada Salida
      Operaciones 0
      Rentabilidad 0%
      Aciertos 0%

      Ajústalo hasta que el número sea bonito. Esa es la parte fácil, y es lo que te enseña cualquier captura de backtest de internet.

      Lo que acabas de aprender → Un backtest es una hipótesis, no un resultado. Cualquiera ajusta una curva hasta que queda bonita con los datos que ya tiene; todo el oficio está en saber cuánto vale ese número con los datos que no tiene.

      Node.jsNestJSFlutterMongoDBRedis + BullMQMotor de backtestPaper trading

      Acto tres

      Y los modelos por debajo

      Todo hasta aquí mueve una lectura que ya existe. Este inventa la siguiente — y es donde las dos mitades de este sitio resultan ser una sola. Entrené el modelo con datos de mercado; corre aquí sobre una sonda de suelo que enterré años antes, sin cambiar una línea, porque una serie temporal no sabe qué mide.

      08 · MENTE StockGAN · 2021

      Enseña a la máquina a adivinar qué viene después

      En 2021 construí desde cero una red generativa adversaria para series temporales — el trabajo en el que mi posgrado en IA y mi máster en Bioingeniería dejan de ser líneas en un diploma — dos redes, una intentando inventar la siguiente lectura y otra intentando pillarla mintiendo. La entrené con datos financieros. Aquí corre sobre una sonda de suelo que diseñé y enterré años antes, sin cambiar nada, porque una serie temporal no sabe qué mide.

      gan_model.py — train_step()

      En 2021 escribí una GAN que pronostica el siguiente valor de una serie temporal — generador, discriminador y bucle de entrenamiento desde cero. La entrené con treinta años de barras diarias, porque ahí era donde había histórico limpio y gratis, pero la red lee log-retornos y no tiene ni idea de qué miden los números. Así que aquí está corriendo sobre una sonda de suelo que construí años antes de todo esto. Lo que vale la pena mostrar es esto: el discriminador nunca juzga una lectura prevista por sí sola. La lectura se pega a las nueve reales que vinieron antes, y la red tiene que decir si la ventana entera salió del sensor. Ese es el juego. Tú eres el discriminador.

      StockGAN · LSTM(128) · ventana de 10 · entrenada con 30 años de barras diarias, corriendo aquí sobre soil-1

      Una de estas dos ventanas termina con una lectura que la sonda tomó de verdad. La otra termina con una que inventó el generador. ¿Cuál es cuál?

      Rondas 0
      El discriminador
      Engañó al crítico

      Elige una. Nueve lecturas reales, y una décima que es la sonda o mi generador.

      1 − λ · MSE λ · critic lo que publiqué · 0.60
      Cerca de la verdad
      Se mueve como el sensor
      Lo que hay de verdad en el archivo generador LSTM(128, stateful) → Dropout(0.3) → Dense(n_features) discriminador LSTM(128, stateful) → Dropout(0.1) → Dense(1, sigmoid)

      RMSprop con decaimiento polinómico de 2.5e-4 a 1e-4 a lo largo de mil pasos. Entropía cruzada con label smoothing en 0.2, para que al discriminador nunca se le permita estar completamente seguro de nada. Objetivo real y objetivo generado multiplicados por ruido con σ = 0.25 durante el entrenamiento, para que no gane notando qué números están demasiado limpios. Doce indicadores — MACD, EMA, RSI, Bollinger, ROC, volatilidad — reducidos por PCA y XGBoost antes de que la red viera nada; en una sonda de suelo esos mismos doce se convierten en medias móviles y tasa de variación de la humedad, y ese es justamente el punto: la arquitectura cambió de dominio y el pipeline de features apenas parpadeó. Las funciones de pérdida de esta página son portes de las reales, constantes incluidas; el generador de aquí es un sustituto que reproduce el dilema sin meter 128 unidades LSTM en tu navegador.

      Lo que acabas de aprender → Dos modelos, una función de pérdida y una perilla entre ellos. Exígele al pronosticador que quede cerca y responde con el promedio de todo, que es seguro e inútil. Exígele que sea creíble y responde con algo que se mueve como el sensor y no sabe nada. Cada modelo que entrego es ese intercambio hecho a propósito, escrito y medido.

      TensorFlow / KerasGANsLSTMSeries temporalesSelección de features con PCA y XGBoost

      Acto cuatro

      Y entonces salió de la pantalla

      Y ahora la medida es el objeto. En Eldorado, en macOS y visionOS, una cosa tridimensional deja de ser algo que un ingeniero dibujó y pasa a ser algo que una cámara midió — el mismo movimiento que hice en 2019, repetido, ahora sobre el mundo.

      09 · CAMPO Eldorado · 2022 → hoy

      Y ahora el objeto está hecho de luz

      El capítulo anterior inventó el siguiente número de una serie. Este deja de tratar el mundo como algo que alguien dibuja. Una escena deja de ser una superficie que alguien modeló y pasa a ser una nube de gaussianas orientadas, ajustadas por descenso de gradiente hasta que sus renders coinciden con las fotografías — lo que significa que nadie la dibuja, y no queda malla alguna que convertir. Las reconstrucciones reales empiezan con una cámara recorriendo una sala. La demo de abajo deliberadamente no: usa una pieza que yo mismo diseñé e imprimí, porque la única manera de ver qué cambia el método es mirar el mismo objeto construido de las dos formas.

      gaussian splatting · visor

      Un objeto, construido dos veces. A un lado del interruptor es la malla con la que se imprimió la pieza. Al otro es un campo de gaussianas sobre esa misma malla — repartidas aquí en vez de ajustadas, para que los dos queden lado a lado y la diferencia sea el método y nada más.

      ↔ arrastra para girar

      cargando la malla…

      Primitivas
      Hechas a mano

      997 triángulos, cada uno colocado por alguien en SolidWorks en 2017.

      Lo que acabas de aprender → Una malla dice dónde están las superficies, y un ingeniero dibujó cada una. Un campo de splats dice dónde está la luz, y nadie dibujó nada de eso — se ajustó hasta que el render dejó de contradecir a la fotografía.

      Gaussian Splatting3DGSvisionOSmacOSPyTorchCUDAFotogrametríaCampos de radiancia

      10 · TRAYECTORIA

      Las pruebas

      Nueve capítulos de demostración. Este es solo evidencia. Arrastra por los años y mira qué estaba corriendo a la vez — porque el titular honesto de esta línea de tiempo no es ningún empleo concreto, es lo poco que hubo uno solo.

      timeline · 2012 → 2026
      2026
      ← arrastra el año →

      Formación

      Ing. de Control y Automatización PUC-PR
      Máster en Bioingeniería PUC-PR
      Posgrado de especialización, Desarrollo iOS Apple Developer Academy
      Especialización en Inteligencia Artificial Aplicada PUC-PR

      Investigación

      Becario, Laboratorio de Automatización y Sistemas PUC-PR · LAS
      Ingeniero de Software — investigación aplicada, IA y salud embebida Fundação Araucária

      Industria

      Desarrollador de Software, autónomo Self-employed
      Trainee · Apple Residency Eldorado · Apple
      Desarrollador Júnior · Proyecto Apple Eldorado · Apple
      Tech Lead · Apple Internship Eldorado · Apple
      Desarrollador Full Stack · Proyecto Apple Eldorado · Apple
      Desarrollador Sénior · Proyecto Apple Eldorado · Apple
      Technology Advisor · Proyecto Renault GHEL

      Emprendimientos

      Consejero Administrativo — el holding familiar RJ Meneghel Holding
      COO y Cofundador Tradx

      De trainee a sénior en cuatro años

      Entré en el proyecto Apple en 2022 como trainee y sigo en el mismo producto, hoy como sénior. Nadie asciende por calendario: cada escalón vino de coger algo que nadie quería — el proceso de release, la suite inestable, la pantalla que todos reescribieron dos veces — y convertirlo en un problema fácil para los demás. Arrastra el año y mira la franja de industria: las cuatro barras seguidas son el mismo producto y el mismo equipo, vistos desde cuatro niveles de responsabilidad distintos.

      Seis empresas

      Fundadas o cofundadas. Algunas crecieron, otras no — que es justamente el motivo de fundar seis.

      • Tradx Cofundador · fintech
      • Agrom.IO Fundador · agritech
      • Dommuz Cofundador · casa inteligente
      • Psiu Cofundador · proptech
      • Hubli Cofundador · edtech
      • PreditChart Cofundador · fintech

      Publicado y premiado

      Seis trabajos académicos, tres de ellos con revisión por pares, y ninguno sobre móvil — y ese es justamente el punto. Ahí aprendí a equivocarme en público: a describir un método con precisión suficiente para que otro lo ataque, aceptar la revisión y cambiar la afirmación. Ese hábito vale más en un code review que cualquiera de los resultados.

      • Instrumento para la Medición de Factores y/o Elementos Naturales del Suelo 2016 · Control & Automation Engineering, PUC-PR
      • A Tool to Select FES Parameters for chronic SCI 2019 · 41st Annual International Conference of the IEEE EMBS · leer el artículo ↗ · doi:10.1109/EMBC.2019.8857421 · código ↗
      • Indoors Wi-Fi Fall Detector Buckle for the Elderly 2019 · Advanced Materials Proceedings, 4(1), 40–45 · leer el artículo ↗ · doi:10.5185/amp.2019.1450 · código ↗
      • Aplicación, in silico, de estímulos eléctricos en un modelo neuromuscular compatible con lesión medular crónica 2019 · M.Sc. dissertation in Bioengineering, PUC-PR · leer el artículo ↗ · código ↗
      • Modelado matemático del sistema de mantenimiento del equilibrio mediante identificación de sistemas 2018 · V Congresso Brasileiro de Eletromiografia e Cinesiologia · X Simpósio de Engenharia Biomédica · leer el artículo ↗
      • Predicción de Series Financieras con Redes Generativas Adversarias 2020 · Applied AI specialization, PUC-PR

      En la prensa

      • Hubli, creado en la Apple Developer Academy — Apple me nombra entre sus cinco creadores Apple Newsroom · 2021 · leer el artículo ↗
      • Neon Wave, juego de iOS cubierto por MacMagazine MacMagazine · 2020 · leer el artículo ↗

      Construyo la app, y construyo aquello con lo que habla. La pantalla, el estado en ella, la API que la llena, la base de datos debajo, y el modelo que lo lee. La mayoría domina una de esas. Yo he entregado todas, y sigo sabiendo cuál es la parte difícil.

      Con qué trabajo

      Todo lo de abajo está acreditado por algún capítulo de esta página o está en un repositorio público. Nada aquí es aspiracional.

      iOS · Swift
      Swift · SwiftUI · UIKit · visionOS · macOS · Swift Concurrency · async/await · actors · Combine · MVVM · Coordinators · Clean Architecture · Dependency injection · SwiftData · Core Data · URLSession · Swift Package Manager · XCTest · XCUITest · Instruments · Xcode · App Store Connect · TestFlight · VoiceOver · Dynamic Type · Localization · Push notifications · Deep linking
      Multiplataforma
      Flutter · Dart · Riverpod · Bloc · Provider · Kotlin · Jetpack Compose · Android · Platform channels · Offline-first · State management · Widget testing
      Publicación y release
      fastlane · Xcode Cloud · GitHub Actions · CI/CD · App Store review · Phased release · Firebase Crashlytics · Crash-free rate · Analytics · Feature flags · Semantic versioning
      Lenguajes
      Swift · Dart · Kotlin · Java · TypeScript · JavaScript · Python · C · C++ · MATLAB · SQL
      Backend y APIs
      Node.js · NestJS · Express · Java · REST APIs · WebSockets · Microservices · OAuth 2.0 · JWT · API versioning · Distributed systems · System design · Event-driven architecture · API design
      Datos
      MongoDB · PostgreSQL · Redis · BullMQ · Database design · Indexing and query planning · Time series · Data pipelines
      Cloud y DevOps
      AWS · Amazon S3 · CloudFront · Route 53 · AWS IoT Core · IAM · EC2 · Docker · Kubernetes · Terraform · Infrastructure as Code · GitHub Actions · CI/CD · OIDC · Observability · Scalability · Linux
      Web
      Astro · HTML · CSS · Accessibility · Design systems · Responsive layout
      IA y 3D
      Machine learning · PyTorch · TensorFlow · Keras · GANs · LSTM · Deep learning · Feature selection · PCA · XGBoost · Gaussian Splatting · 3DGS · CUDA · Photogrammetry · Computer vision
      Embebidos y hardware
      Embedded C · ESP32 · PIC18F4550 · ATmega · PWM · ADC · MQTT · IoT · PCB design · EAGLE · Altium · SolidWorks · CAD · 3D printing · Design for manufacturing
      Forma de trabajar
      Git · Code review · Unit testing · Technical leadership · Mentoring · Product engineering · Backtesting · Paper trading

      Hablemos

      Si algo de esto tiene la forma de un problema que tienes, quiero saberlo. Correo o LinkedIn, lo que prefieras — el CV es un PDF de dos páginas y el código está en GitHub.