Jerson Moreno

Full Stack Developer & Emprendedor

Construyo soluciones digitales escalables y experiencias web de alto impacto.

Livewire vs Inertia.js en Laravel: cómo elijo yo (y cómo puedes elegir tú)

Si estás empezando en el mundo full-stack con Laravel, es normal que te sientas un poco abrumado. De repente escuchas términos como “TALL stack”, “Inertia”, “reactividad”, “SPA”… y lo único que quieres es saber qué herramienta instalar para empezar a programar.

La buena noticia es que en Laravel tenemos dos “superpoderes” para crear aplicaciones modernas sin volvernos locos: Livewire e Inertia.js.

Ambos sirven para lo mismo (hacer que tu web se sienta rápida y fluida como una aplicación móvil), pero funcionan de forma muy distinta. Después de probar ambos, he llegado a la conclusión de que la mejor forma de elegir no es por “cuál es mejor”, sino por “qué estás construyendo”.

Aquí te cuento cómo lo veo yo, de tú a tú.


1. Paneles Internos y Herramientas de Gestión (Backoffice)

Mi elección: Livewire

Si te han pedido un panel para gestionar usuarios, un inventario o una herramienta interna para una empresa, Livewire es el rey.

¿Por qué? Porque en estos proyectos lo que importa es la velocidad de desarrollo. Con Livewire no tienes que salirte de Laravel. Escribes tu lógica en PHP, usas tus vistas de Blade de toda la vida y, casi por arte de magia, todo se vuelve interactivo.

Además, si usas Filament (una librería increíble basada en Livewire), puedes tener un panel de administración profesional funcionando en una tarde. Para un perfil full-stack que quiere resultados rápidos sin pelearse con configuraciones complejas de JavaScript, esta es la vía rápida.


2. E-commerce y Sitios con mucho SEO

Mi elección: Livewire

Si vas a montar una tienda online o un sitio donde necesitas que Google te encuentre fácilmente (SEO), Livewire tiene una ventaja natural: renderiza todo en el servidor.

Cuando Google visita tu página hecha con Livewire, ve el contenido HTML directamente, igual que en una web tradicional. Con Inertia, aunque se puede configurar el renderizado en el servidor (SSR), es un paso extra que a veces da dolores de cabeza si estás empezando. Con Livewire, el SEO viene “de serie”.


3. Aplicaciones con UI muy pesada o “tipo Dashboard” complejo

Mi elección: Inertia.js

Imagina que estás creando una herramienta de edición de fotos online, un gestor de tareas con mucho “drag and drop” (arrastrar y soltar) o una red social con mil notificaciones en tiempo real. Aquí es donde Inertia.js brilla.

Inertia te permite usar Vue o React dentro de Laravel. Estos frameworks de JavaScript están diseñados específicamente para manejar interfaces muy complejas y estados que cambian constantemente en el navegador.

Si tu aplicación va a tener muchísima lógica en el “cliente” (el navegador del usuario), usar Inertia te da acceso a miles de librerías de Vue o React que ya resuelven estos problemas.


4. Proyectos SaaS (Software como Servicio)

Mi elección: Depende de tu stack favorito

Aquí es donde la cosa se pone interesante. Un SaaS suele ser una mezcla de todo lo anterior.

  • Si vienes del mundo Backend: Ve por Livewire. Te sentirás más cómodo controlando todo desde PHP y usando Alpine.js para los toques de JavaScript.
  • Si vienes del mundo Frontend: Ve por Inertia. Poder usar Vue o React mientras aprovechas toda la potencia de Laravel en el backend es, sencillamente, una delicia.

Entonces, ¿por cuál empiezo si soy principiante?

Si estás dando tus primeros pasos como full-stack, mi consejo personal es este: Empieza por Livewire.

La curva de aprendizaje es mucho más suave. No tienes que aprender cómo funciona un compilador de JavaScript complejo, ni preocuparte por cómo pasar datos entre dos mundos distintos. Todo se queda “en casa”, en Laravel.

Una vez que te sientas cómodo creando componentes y manejando la reactividad básica, dale una oportunidad a Inertia. Entender ambos te convertirá en un desarrollador mucho más versátil.

En resumen:

  • ¿Quieres velocidad y quedarte en PHP? Livewire.
  • ¿Quieres el poder de Vue/React y una experiencia SPA total? Inertia.
  • ¿Quieres terminar el proyecto hoy mismo? Probablemente Livewire.

Al final del día, lo más importante no es la herramienta, sino que te sientas cómodo usándola. ¡Instala ambos en proyectos de prueba y mira cuál te saca una sonrisa al programar!


Mi checklist rápido (decisión en 60 segundos)

Si respondes “sí” a varias de estas, ya tienes una pista:

  • ¿Quiero minimizar JavaScript y trabajar mayormente con Blade? → Livewire
  • ¿Mi UI depende de estado complejo y componentes frontend avanzados? → Inertia
  • ¿Quiero mejorar solo algunas partes de una app existente? → Livewire
  • ¿Mi equipo es más “frontend” y ya domina Vue/React/Svelte? → Inertia
  • ¿Necesito SEO fácil sin montar SSR extra? → Livewire
  • ¿Busco una experiencia SPA completa y extensible con librerías del ecosistema? → Inertia

Conclusión (mi consejo más honesto)

Cuando dos opciones son buenas, la decisión rara vez es “técnica pura”. Muchas veces es:

  • productividad real,
  • comodidad del equipo,
  • y disfrute construyendo (sí, importa).

Mi recomendación práctica es la misma que yo aplico: arma una mini feature con ambas (una pantalla real, no “hola mundo”) y mide: ¿con cuál avanzas más rápido y con menos fricción?

Lo bueno: ambas son apuestas sólidas dentro de Laravel, con soporte y comunidad. Elijas la que elijas, puedes construir algo muy serio.

Continue

Hoja de ruta del desarrollador Full-Stack para 2026

DE PHP 8.5 A LA IA GENERALISTA

Hace tres años, si me hubieran dicho que en 2026 estaría usando herramientas de IA para revisar mis pull requests y generar tests automáticamente, probablemente me hubiera reído. Pero aquí estamos, y el desarrollo web se ha transformado en algo que ni siquiera reconoceríamos hace una década.

Lo interesante es que, a pesar de toda esta evolución tecnológica, los fundamentos siguen siendo los mismos. Y si estás empezando o buscando consolidarte como desarrollador full-stack este año, hay algunas cosas que necesitas saber.

PHP EN 2026: MÁS VIVO QUE NUNCA

Sí, ya sé. PHP tiene mala reputación entre algunos desarrolladores que se quedaron con la imagen del lenguaje de hace 15 años. Pero PHP 8.5 es una bestia completamente diferente. Los tipos nativos, las propiedades readonly, los enums, y todo el ecosistema moderno han convertido PHP en un lenguaje que compite de tú a tú con cualquier otro.

Laravel, por su parte, sigue siendo mi framework preferido para proyectos que necesitan salir rápido sin sacrificar calidad. No es el framework más “cool” del mercado —ese título probablemente lo tenga alguna tecnología nueva cada tres meses—, pero es increíblemente productivo. Y en proyectos reales, con clientes reales y deadlines reales, la productividad vale más que la novedad.

Laravel Herd Pro: El Entorno Que No Sabías Que Necesitabas

Hablemos de algo que ha cambiado mi vida diaria: Laravel Herd Pro. Si has lidiado con Docker, Vagrant, Homestead, o cualquier otra solución para configurar tu entorno local, sabes el dolor que puede ser. Herd Pro eliminó todo eso.

Instalas, abres tu proyecto, y funciona. Sin configurar NGINX, sin problemas de permisos, sin preguntarte por qué el puerto 80 está ocupado otra vez. Tiene PHP switching incorporado, bases de datos, servicios de correo para testing… todo lo que necesitas sin la ceremonia.

No es una exageración decir que me ha ahorrado horas cada semana. Y esas horas las puedo usar para escribir código o, seamos honestos, para tomar otro café.

LOS FUNDAMENTOS NUNCA MUEREN

Hay algo que me sigue sorprendiendo cuando entrevisto developers: la cantidad de gente que sabe usar frameworks complejos pero no entiende por qué un array empieza en el índice 0.

La respuesta corta es: porque así funcionan los punteros en memoria. Cuando declaras un array, el nombre del array es básicamente un puntero a la primera posición de memoria. El índice es un offset —una distancia— desde ese punto inicial. Entonces, el primer elemento está a distancia 0 del inicio. El segundo está a distancia 1, y así sucesivamente.

¿Por qué importa esto? Porque entender cómo funciona la memoria te ayuda a escribir código más eficiente y a debuggear problemas que de otra forma parecerían magia negra.

Estructuras de Datos: El Arte de Organizar el Caos

En PHP moderno, trabajar con estructuras de datos complejas y anidadas es pan de cada día. APIs que retornan JSON con arrays dentro de objetos dentro de arrays… ya sabes de qué hablo.

Lo que he aprendido después de años lidiando con esto es que necesitas una estrategia clara. No se trata solo de usar array_map o collection->filter() —aunque son herramientas poderosas—, sino de pensar en cómo estructuras tus datos desde el principio.

Por ejemplo, cuando consumes una API externa, mi primer instinto es crear Data Transfer Objects (DTOs) simples que representen esa estructura. No estoy hablando de usar paquetes complejos ni soluciones over-engineered. A veces es solo una clase con propiedades públicas tipadas y un constructor que toma el array crudo. Pero esa capa de abstracción te salva cuando la API decide cambiar un campo o cuando necesitas transformar los datos para tu aplicación.

La clave está en encontrar el balance entre la simplicidad y la estructura. He visto proyectos que se vuelven imposibles de mantener porque todo es un array anidado sin tipo, y también he visto proyectos que se ahogan en su propia arquitectura porque cada estructura tiene 5 capas de abstracción.

EL ECOSISTEMA FRONT-END DE 2026: UN ZOOLÓGICO ORDENADO

Aquí es donde las cosas se ponen interesantes. El front-end moderno es un paisaje fragmentado, pero no de manera caótica como hace unos años. Las cosas se han estabilizado un poco.

React sigue siendo el rey, pero ya no es el React que conocías. Con Server Components y las mejoras en el ecosistema, se ha vuelto más parecido a los frameworks tradicionales del lado del servidor, pero con toda la interactividad del cliente cuando la necesitas. Next.js ha sido fundamental en esta transformación.

Hablando de Next.js, si vas a aprender un framework front-end este año y quieres algo que te dé trabajo inmediatamente, esta es tu mejor apuesta. El routing basado en archivos, la generación estática, el rendering del servidor, el middleware… es todo lo que necesitas para hacer aplicaciones complejas. Eso sí, la curva de aprendizaje puede ser empinada, especialmente si vienes del mundo tradicional de PHP + Blade templates.

Vue y su ecosistema (especialmente Nuxt) siguen siendo opciones sólidas. Personalmente, encuentro Vue más intuitivo que React si vienes de backend. La sintaxis es más limpia, menos “mágica” que JSX. Aunque debo admitir que el mercado laboral está más inclinado hacia React.

Y luego está Astro, que ha ganado muchísima tracción. La idea es simple pero poderosa: envía el mínimo JavaScript posible al navegador. Si estás construyendo contenido estático o sitios con poca interactividad, Astro es brillante. Puedes incluso mezclar componentes de React, Vue, o lo que sea dentro del mismo proyecto.

Como desarrollador full-stack que viene del lado de Laravel, mi recomendación es esta: aprende los fundamentos de JavaScript moderno primero. ES6+, async/await, promises, destructuring. Luego elige UN framework y profundiza. No intentes aprender todos al mismo tiempo.

Yo empecé con Vue porque la sintaxis me resultaba familiar, pero terminé trabajando más con React porque los proyectos lo demandaban. La verdad es que una vez que entiendes uno, los demás se vuelven más fáciles de aprender porque los conceptos fundamentales son los mismos.

CLOUDFLARE Y EL EDGE COMPUTING: EL PRESENTE, NO EL FUTURO

Si hay algo que ha cambiado mi forma de pensar sobre arquitectura en los últimos años, es el edge computing. Y Cloudflare está liderando eso de manera impresionante.

Cloudflare Workers te permite ejecutar código en más de 300 ubicaciones alrededor del mundo, más cerca de tus usuarios que cualquier servidor tradicional. La latencia se reduce drásticamente. Estamos hablando de respuestas en milisegundos, sin importar si tu usuario está en Tokio o en Buenos Aires.

Lo que más me gusta es que no es solo para casos de uso exóticos. Puedo usar Workers para hacer cosas prácticas: transformar imágenes al vuelo, cachear respuestas de API inteligentemente, ejecutar lógica de autenticación antes de que la request llegue a mi servidor principal, o incluso servir aplicaciones completas.

La integración con Laravel es sorprendentemente buena. Puedes tener tu aplicación principal en Laravel, pero usar Workers para manejar tareas específicas que necesitan estar en el edge. Por ejemplo, un endpoint de geolocalización que retorna contenido diferente según la ubicación del usuario, o un sistema de A/B testing que se ejecuta antes de que el HTML llegue al navegador.

Cloudflare Pages también merece una mención. Si estás usando alguno de esos frameworks front-end que mencioné antes, desplegar en Pages es ridículamente simple. Git push, y tu sitio está live. Con SSL, CDN global, y todo lo demás incluido.

LA IA EN EL DESARROLLO: HERRAMIENTA, NO REEMPLAZO

Seamos claros: GitHub Copilot, ChatGPT, y todas estas herramientas de IA no van a reemplazarte. Pero sí van a hacer que alguien que las usa sea más productivo que tú.

Yo uso IA todos los días. Para generar boilerplate, para revisar mi código, para escribir tests, para documentar funciones complejas. No le dejo escribir la lógica de negocio crítica, pero sí todo el código repetitivo que consume tiempo.

La clave es saber qué delegar y qué no. La IA es excelente para patrones comunes, terrible para contexto específico de negocio. Trátala como un junior developer muy rápido: puede hacer mucho trabajo pesado, pero necesita supervisión.

CONSEJOS PARA CRECER COMO FULL-STACK EN 2026

Después de todo esto, aquí está lo que realmente importa:

Primero, domina un stack completo antes de saltar a otro. Laravel + Vue o Laravel + React + MySQL + Cloudflare. Haz proyectos reales, no solo tutoriales. La diferencia entre saber usar un framework y saber construir con él solo la descubres cuando enfrentas problemas reales.

Segundo, aprende a leer código tanto como escribirlo. Los mejores developers que conozco pasan más tiempo leyendo que escribiendo. Clona repos populares, lee su código, entiende por qué tomaron ciertas decisiones.

Tercero, no menosprecies los fundamentos. Algoritmos, estructuras de datos, patrones de diseño. No necesitas memorizarlos todos, pero entender cuándo usar qué te separa de alguien que solo conoce frameworks.

Cuarto, construye en público. No tiene que ser open source complicado. Puede ser un blog técnico, responder preguntas en Stack Overflow, o compartir TILs (Today I Learned) en Twitter. Enseñar te obliga a entender profundamente.

Y finalmente, no te agobies. El desarrollo web es abrumador porque hay demasiado que aprender. Nadie sabe todo. Yo llevo años en esto y sigo aprendiendo cosas nuevas cada semana. La clave no es saberlo todo, sino saber aprender rápido cuando lo necesites.

El 2026 es un año increíble para ser desarrollador full-stack. Las herramientas nunca han sido mejores, la demanda nunca ha sido más alta, y las oportunidades para construir cosas geniales están por todas partes. Solo necesitas empezar, seguir aprendiendo, y no rendirte cuando las cosas se pongan difíciles (porque se van a poner difíciles, créeme).

Continue

Cómo Consumir la API REST de WordPress desde Astro

CÓMO CONSUMIR LA API REST DE WORDPRESS DESDE ASTRO

WordPress incluye una API REST integrada que permite acceder a todo el contenido de forma programática. En este artículo aprenderás a consumir esta API desde Astro de manera simple y segura.

¿QUÉ ES WORDPRESS HEADLESS?

WordPress Headless es usar WordPress únicamente como gestor de contenido (CMS), mientras que el frontend se construye con otra tecnología, en este caso Astro. La comunicación entre ambos se realiza mediante la API REST de WordPress.

CONFIGURACIÓN INICIAL

1. Variables de Entorno

Crea un archivo .env.development para almacenar la URL de tu API:

API_URL=https://linen-antelope-447525.hostingersite.com/wp-json/wp/v2

Para producción, crea .env.production:

API_URL=https://tu-sitio-wordpress.com/wp-json/wp/v2

Astro cargará automáticamente el archivo correcto según el entorno.

VALIDACIÓN CON ZOD

Es fundamental validar los datos que vienen de la API. Zod es perfecto para esto y viene integrado con Astro.

Definir Schemas

Crea un archivo src/types/index.ts con los schemas:

import { z } from "astro:content";

// Schema base para contenido de WordPress
export const BaseWPSchema = z.object({
    id: z.number(),
    slug: z.string(),
    title: z.object({ rendered: z.string() }),
    content: z.object({ rendered: z.string() }),
});

// Schema para un post individual
export const PostItemSchema = BaseWPSchema.extend({
    date: z.string(),
    excerpt: z.object({ rendered: z.string() }),
});

// Schema para array de posts
export const PostsSchema = z.array(PostItemSchema);

// Tipo TypeScript exportado
export type Post = z.infer<typeof PostItemSchema>;

CONSUMIENDO LA API

Obtener Lista de Posts

En cualquier componente .astro, usa el frontmatter para hacer fetch:

---
import { PostsSchema } from "@/types";

const url = `${import.meta.env.API_URL}/posts`;
const response = await fetch(url);
const json = await response.json();
const posts = PostsSchema.safeParse(json);

if (!posts.success) {
    console.error("Error validando posts:", posts.error);
}

const validPosts = posts.success ? posts.data : [];
---

<div>
    {validPosts.map((post) => (
        <article>
            <h2 set:html={post.title.rendered} />
            <div set:html={post.excerpt.rendered} />
            <a href={`/blog/${post.slug}`}>Leer más</a>
        </article>
    ))}
</div>

Obtener Post Individual

Para páginas dinámicas, usa [slug].astro:

---
import { PostItemSchema } from "@/types";

export const prerender = false; // SSR activado

const { slug } = Astro.params;
const url = `${import.meta.env.API_URL}/posts?slug=${slug}`;
const response = await fetch(url);
const json = await response.json();
const post = PostItemSchema.safeParse(json[0]);

if (!post.success) {
    return Astro.redirect("/404");
}

const { title, content, date } = post.data;
---

<article>
    <h1 set:html={title.rendered} />
    <time>{date}</time>
    <div set:html={content.rendered} />
</article>

TRABAJANDO CON IMÁGENES

WordPress devuelve las imágenes destacadas en diferentes tamaños. Define un schema para validarlas:

const imageSchema = z.object({
    url: z.string().url(),
    width: z.number(),
    height: z.number(),
});

const featureImageSchema = z.object({
    thumbnail: imageSchema,
    medium: imageSchema,
    large: imageSchema,
    full: imageSchema,
});

export const PostItemSchema = BaseWPSchema.extend({
    featured_images: featureImageSchema.optional(),
});

Configurar Dominios Permitidos

En astro.config.mjs, permite las imágenes de WordPress:

export default defineConfig({
    image: {
        domains: ["tu-sitio-wordpress.com"]
    },
});

Usar las Imágenes en Componentes

---
import { Picture } from "astro:assets";
---

{post.featured_images?.full.url && (
    <Picture
        src={post.featured_images.full.url}
        alt={post.title.rendered}
        width={post.featured_images.full.width}
        height={post.featured_images.full.height}
        formats={["avif", "webp"]}
    />
)}

CUSTOM FIELDS CON ACF

Si usas Advanced Custom Fields, añade los campos personalizados al schema:

export const ProjectSchema = BaseWPSchema.extend({
    acf: z.object({
        url: z.string().url(),
        github_repo: z.string().optional(),
    }),
    technologies: z.array(z.string()),
});

Accede a ellos normalmente:

---
const { acf, technologies } = project;
---

<a href={acf.url}>Ver Proyecto</a>
<ul>
    {technologies.map(tech => <li>{tech}</li>)}
</ul>

ENDPOINTS COMUNES

La API REST de WordPress ofrece varios endpoints:

  • /posts – Artículos del blog

  • /pages – Páginas

  • /categories – Categorías

  • /tags – Etiquetas

  • /media – Archivos multimedia

  • /users – Usuarios

Todos siguen el mismo patrón de consumo.

MEJORES PRÁCTICAS

  1. Siempre valida con Zod: Usa .safeParse() en lugar de .parse() para manejar errores gracefully.

  2. Maneja errores: Redirige a 404 o muestra mensajes apropiados cuando falle la validación.

  3. Usa SSR cuando sea necesario: Añade export const prerender = false en páginas que necesiten datos en tiempo real.

  4. Tipado fuerte: Exporta tipos desde tus schemas con z.infer<> para aprovechar TypeScript.

  5. Variables de entorno: Nunca hardcodees URLs de APIs, usa variables de entorno.

Continue

Guía Introductoria a C++: De Cero a Héroe

Introducción: ¿Por qué C++ sigue siendo relevante hoy?

C++ es uno de los lenguajes de programación más potentes y versátiles jamás creados. A pesar de tener décadas de existencia, su relevancia en el mundo del desarrollo de software no ha disminuido. Desde sistemas operativos y videojuegos de alto rendimiento hasta aplicaciones financieras de baja latencia y sistemas embebidos, C++ es la columna vertebral de innumerables tecnologías que usamos a diario.

Para los desarrolladores principiantes, aprender C++ ofrece una comprensión profunda de cómo funciona realmente el software a bajo nivel, incluyendo la gestión de memoria y la optimización del rendimiento. Para los programadores intermedios, dominar C++ abre las puertas a áreas especializadas y muy demandadas. Esta guía te llevará a través de los fundamentos y las características más importantes que necesitas para empezar tu viaje con C++.

Un Vistazo a su Historia y Evolución

Para apreciar C++, es útil conocer su origen.

H3 Orígenes: De C a C++

C++ fue diseñado en 1979 por Bjarne Stroustrup en los Laboratorios Bell. Originalmente llamado “C con Clases”, su objetivo era añadir características de programación orientada a objetos (POO) al ya popular lenguaje C, sin sacrificar la velocidad y la flexibilidad que C ofrecía. Esta compatibilidad con C es una de sus señas de identidad: casi cualquier programa en C es también un programa válido en C++.

Evolución y Estándares Modernos

Con el tiempo, C++ ha evolucionado enormemente. El comité de estándares de C++ ha introducido actualizaciones significativas para modernizar el lenguaje. Las más notables son C++11, C++14, C++17 y C++20, que han añadido características como punteros inteligentes, expresiones lambda, una mejor gestión de la concurrencia y mucho más. Hoy en día, hablar de “C++ Moderno” se refiere al uso de estas nuevas características para escribir código más seguro, limpio y eficiente.

Configurando tu Entorno de Desarrollo

Para empezar a programar en C++, necesitas dos cosas principales: un compilador y un editor de código o IDE.

  • Compilador: Es el programa que traduce tu código C++ a código máquina que el ordenador puede ejecutar. El más común es GCC (GNU Compiler Collection), que es gratuito y viene preinstalado en la mayoría de los sistemas Linux. Para Windows, puedes usar MinGW (una versión de GCC para Windows) o el compilador de Visual Studio. En macOS, Clang (parte de Xcode) es la opción estándar.
  • Editor de Código/IDE: Puedes usar un editor simple como Visual Studio Code (con la extensión de C++), Sublime Text o Atom. O bien, un Entorno de Desarrollo Integrado (IDE) completo como Visual Studio (en Windows), CLion (multiplataforma) o Code::Blocks (gratuito y multiplataforma).

Para verificar tu instalación, puedes compilar y ejecutar el clásico “Hola, Mundo”.

#include <iostream>

int main() {
    std::cout << "¡Hola, Mundo!" << std::endl;
    return 0;
}

Guarda este código como hola.cpp y compílalo desde la terminal con g++ hola.cpp -o hola. Luego, ejecútalo con ./hola. Si ves el mensaje en pantalla, ¡estás listo para continuar!

Conceptos Fundamentales de C++

Todo lenguaje tiene una sintaxis y unas reglas básicas. Aquí repasamos las más importantes en C++.

Variables y Tipos de Datos

Las variables son contenedores para almacenar datos. En C++, debes declarar el tipo de dato que una variable contendrá.

  • int: para números enteros (ej: int edad = 25;).
  • double / float: para números con decimales (ej: double precio = 19.99;).
  • char: para un solo carácter (ej: char inicial = 'A';).
  • bool: para valores de verdadero o falso (ej: bool esValido = true;).
  • std::string: para cadenas de texto (requiere #include <string>).

Estructuras de Control

Permiten dirigir el flujo de tu programa.

  • Condicionales (if-else): Ejecutan código basado en una condición.
int edad = 18;
if (edad >= 18) {
    std::cout << "Eres mayor de edad." << std::endl;
} else {
    std::cout << "Eres menor de edad." << std::endl;
}
  • Bucles (for, while): Repiten un bloque de código.
// Bucle for para contar hasta 5
for (int i = 1; i <= 5; ++i) {
    std::cout << "Número: " << i << std::endl;
}

// Bucle while
int contador = 0;
while (contador < 3) {
    std::cout << "Iteración de while." << std::endl;
    contador++;
}

Funciones

Las funciones son bloques de código reutilizables que realizan una tarea específica. Ayudan a organizar el código y hacerlo más modular.

#include <iostream>

// Declaración de la función
int sumar(int a, int b) {
    return a + b;
}

int main() {
    int resultado = sumar(5, 3); // Llamada a la función
    std::cout << "La suma es: " << resultado << std::endl; // Imprime 8
    return 0;
}

El Poder de la Programación Orientada a Objetos (POO)

La POO es el paradigma que hizo famoso a C++. Permite modelar entidades del mundo real como “objetos” que tienen propiedades (atributos) y comportamientos (métodos).

Clases y Objetos

Una clase es una plantilla para crear objetos. Un objeto es una instancia de una clase.

#include <iostream>
#include <string>

class Coche {
public:
    // Atributos
    std::string marca;
    int anio;

    // Método
    void mostrarInfo() {
        std::cout << "Marca: " << marca << ", Año: " << anio << std::endl;
    }
};

int main() {
    // Crear un objeto (instancia) de la clase Coche
    Coche miCoche;
    miCoche.marca = "Toyota";
    miCoche.anio = 2021;

    miCoche.mostrarInfo(); // Llama al método del objeto
    return 0;
}

La Biblioteca de Plantillas Estándar (STL)

La STL (Standard Template Library) es una de las características más potentes de C++. Es una colección de clases y funciones que proporcionan estructuras de datos y algoritmos de uso común.

  • Contenedores: Estructuras de datos para almacenar colecciones de objetos. Los más comunes son std::vector (un array dinámico), std::list (una lista doblemente enlazada), y std::map (un diccionario clave-valor).
  • Algoritmos: Funciones para operar sobre los contenedores, como std::sort (para ordenar), std::find (para buscar) y std::for_each (para iterar).
  • Iteradores: Objetos que actúan como punteros para recorrer los elementos de un contenedor.

Usar la STL te ahorra tiempo y te permite escribir código más robusto y eficiente.

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> numeros = {5, 2, 8, 1, 9};

    // Ordenar el vector
    std::sort(numeros.begin(), numeros.end());

    // Imprimir el vector ordenado
    std::cout << "Números ordenados: ";
    for (int num : numeros) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

Conclusión: Tus Próximos Pasos en C++

Hemos cubierto los pilares de C++: su historia, configuración, sintaxis básica, el paradigma de POO y la indispensable STL. Aunque esto es solo la punta del iceberg, ahora tienes una base sólida para seguir construyendo.

¿Qué sigue?

  1. Practica, practica y practica: Resuelve problemas en plataformas como LeetCode o HackerRank.
  2. Profundiza en la gestión de memoria: Aprende sobre punteros, new/delete y, más importante, los punteros inteligentes (std::unique_ptr, std::shared_ptr) de C++ moderno.
  3. Construye un proyecto pequeño: Intenta crear una aplicación de consola simple, como una lista de tareas o una pequeña agenda.

C++ es un lenguaje desafiante pero inmensamente gratificante. Dominarlo no solo te convertirá en un mejor programador, sino que también te dará acceso a algunas de las áreas más emocionantes de la informática. ¡Buena suerte en tu viaje!

Continue

Lo que me hubiera gustado saber sobre punteros en C++ cuando era junior

Lo que me hubiera gustado saber sobre punteros en C++ cuando era junior

Resumen: En este post te cuento, en lenguaje humano, qué son realmente los punteros en C++, por qué asustan tanto al principio y qué cosas me hubiera encantado entender desde el día uno para evitar segfaults y dolores de cabeza.

Nivel: Junior

Introducción

Si estás empezando con C++, es muy probable que la palabra puntero te produzca una mezcla de respeto y miedo.
Y no te culpo: casi todos hemos roto programas (y paciencia) por culpa de un puntero mal usado.

Cuando yo era junior, pensaba que los punteros eran algo “mágico” que había que memorizar,
en vez de entender de verdad qué estaba pasando en memoria.
En este post quiero contarte lo que me hubiera gustado saber desde el principio:

  • Qué es realmente un puntero (sin humo).
  • La diferencia entre una variable normal, una referencia y un puntero.
  • Por qué new, delete y el temido segmentation fault no son brujería.
  • Errores típicos que todos cometemos al empezar (y cómo evitarlos).

Qué es de verdad un puntero

Un puntero no es más que una variable que guarda una dirección de memoria.
Nada más. No es un monstruo, no es una entidad oscura del compilador.
Es como un papelito donde apuntas: “el valor que buscas está en la dirección X”.

Ejemplo básico:


int x = 42;      // variable normal
int* p = &x;     // p es un puntero a int, guarda la dirección de x
  • x guarda el valor 42.
  • p guarda “dónde está x en memoria”.
  • &x significa “la dirección de x”.

Para acceder al valor apuntado por p, usas el operador * (dereferencia):


std::cout << *p << std::endl; // imprime 42

Lo que me hubiera gustado que me dijeran es:
el puntero es un número (dirección), la estrella (*) es cómo vas desde esa dirección al valor real.

Variable, referencia y puntero: misma historia, distintas interfaces

Cuando eres junior, de pronto te tiran esto encima:


int x = 10;
int& ref = x;
int* ptr = &x;

Y tu cabeza dice: “¿Por qué tres formas de hacer lo mismo?”. La clave:

  • Variable normal → guarda un valor.
  • Referencia (&) → es un alias de la variable, no puedes cambiar a qué “apunta”.
  • Puntero (*) → guarda una dirección que SÍ puedes cambiar en tiempo de ejecución.

Ejemplo muy simplificado:


int x = 10;
int y = 20;

int& r = x;   // referencia a x
int* p = &x;   // puntero a x

r = 15;        // modifica x, ahora x = 15
*p = 30;       // también modifica x, ahora x = 30

p = &y;        // ahora p apunta a y
*p = 99;       // modifica y, ahora y = 99

// r no puede "reasignarse" para referenciar y, se queda con x

Lo que me hubiera ayudado mucho: pensar que las referencias son un “puntero constante implícito”,
más seguro y cómodo, mientras que los punteros son más flexibles pero también más peligrosos.

Memoria dinámica: por qué existen new y delete

Otra cosa que me hubiera encantado entender pronto:
por qué necesitamos memoria dinámica. ¿No basta con hacer int x = 5; y ya?

Las variables “normales” suelen vivir en la pila (stack):

  • Se crean cuando entras a una función.
  • Se destruyen automáticamente cuando sales de la función.

Pero a veces quieres que algo viva más allá de esa función:


int* crearNumero() {
    int* p = new int(42);
    return p; // devolvemos un puntero a memoria dinámica
}

int main() {
    int* numero = crearNumero();
    std::cout << *numero << std::endl; // 42
    delete numero; // importante: liberar la memoria
}

Aquí la memoria se reserva en el heap con new, y no se libera sola.
Esa es la razón de ser de delete (y de las fugas de memoria cuando se nos olvida).

El clásico terror: segmentation fault

Uno de los mejores “profesores” que vas a tener en C++ es el temido segmentation fault.
Cada vez que te aparece, es básicamente el sistema operativo diciéndote:
“estás tocando memoria que no es tuya”.

Las causas típicas que me hubiera gustado tener claras desde el día uno:

  • Dereferenciar punteros nulos: int* p = nullptr; *p = 10; // BOOM
  • Acceder fuera de rangos en arrays.
  • Usar punteros colgantes (que apuntan a memoria ya liberada).

Ejemplo de puntero colgante:


int* peligrosito() {
    int x = 10;
    return &x;   // MALA IDEA: x se destruye al salir de la función
}

int main() {
    int* p = peligrosito();
    std::cout << *p << std::endl; // comportamiento indefinido
}

Aquí el puntero p parece válido, pero en realidad apunta a algo que ya no existe.
Este tipo de bug es de los más difíciles de depurar.

Lo que me hubiera gustado saber sobre buenas prácticas con punteros

1. Inicializa SIEMPRE tus punteros

No dejes punteros “a lo que haya por ahí en memoria”.


int* p = nullptr;  // mejor que dejarlo sin inicializar

Y antes de usarlo:


if (p != nullptr) {
    // usa p con tranquilidad razonable
}

2. Cada new debería tener su delete

Como junior, yo hacía new por todos lados y me olvidaba de delete.
Resultado: fugas de memoria como si no hubiera un mañana.


int* p = new int(5);
// ...
delete p;   // si no lo haces, memoria perdida
p = nullptr; // buena práctica: evitar punteros colgantes

Más adelante, lo ideal es usar smart pointers (std::unique_ptr, std::shared_ptr)
para no tener que preocuparte tanto de esto, pero entender la base de new/delete ayuda muchísimo.

3. No uses punteros si una referencia o una variable normal es suficiente

Algo que me habría ahorrado muchos bugs:
no todo tiene que ser un puntero.
A veces una referencia o pasar un objeto por valor es más simple y más seguro.


// Mucho más legible y seguro:
void incrementar(int& x) {
    x++;
}

En lugar de:


void incrementar(int* x) {
    if (x != nullptr) {
        (*x)++;
    }
}

Los punteros tienen sentido cuando realmente necesitas esa flexibilidad:
memoria dinámica, estructuras de datos, interoperabilidad con C, etc.

4. Dibujar la memoria en papel ayuda muchísimo

Una de las cosas que más me ayudó (y que ojalá hubiera hecho antes) fue dibujar lo que está pasando:
cuadritos para variables, flechas para punteros, tachar cuando algo se destruye, etc.

Por ejemplo, para este código:


int x = 10;
int* p = &x;
int* q = p;
*q = 20;

Visualmente:


x: 20
p ----┐
      v
q ----┘

Entender que p y q apuntan al MISMO sitio de memoria hace que el comportamiento del programa deje de ser “mágico”.

Punteros y arrays: otro clásico que confunde

Otra cosa que me confundió muchísimo al principio es que los arrays y los punteros están muy relacionados,
y la sintaxis no ayuda.


int arr[3] = {1, 2, 3};
int* p = arr;       // arr "decáe" a puntero a su primer elemento

Aquí, p apunta a arr[0].
Entonces:

  • *p es arr[0].
  • *(p + 1) es arr[1].
  • *(p + 2) es arr[2].

Y esta es la parte divertida (y confusa):


p[0] == *p;
p[1] == *(p + 1);

Es decir, p[i] es solo azúcar sintáctico para *(p + i).
Cuando entendí esto, todo el tema de arrays + punteros empezó a tener más sentido.

Resumen: lo que realmente me hubiera ahorrado tiempo

  • Un puntero solo guarda una dirección. La magia está en la dereferencia (*).
  • Referencia vs puntero:
    la referencia es un alias seguro; el puntero es flexible pero peligroso.
  • Memoria dinámica: si usas new,
    alguien tiene que hacer delete. Si no, fuga de memoria.
  • Segmentation fault casi siempre es:
    estás tocando memoria que no deberías (puntero nulo, colgante o fuera de rango).
  • No todo necesita ser un puntero: usa referencias y valores cuando puedas.
  • Dibuja la memoria: papel y lápiz son tus amigos cuando estás aprendiendo.

Conclusión

Los punteros son una de las partes más temidas de C++, pero también una de las más poderosas.
Entenderlos bien te abre la puerta a estructuras de datos, optimización y a entender qué pasa “debajo del capó”
cuando programas en otros lenguajes de más alto nivel.

Si estás ahora mismo peleándote con punteros, es normal.
Nos pasa a todos. La clave es practicar con ejemplos pequeños, dibujar lo que pasa en memoria
y no tener miedo a romper cosas en tus pruebas.

En próximos posts podemos ver:

  • Introducción a std::unique_ptr y std::shared_ptr.
  • Cómo implementar estructuras de datos (listas, árboles) usando punteros.
  • Errores típicos con punteros inteligentes y cómo evitarlos.

Recursos recomendados

  • Documentación de cppreference sobre punteros y referencias.
  • Tutoriales visuales de memoria en C++ en YouTube — los diagramas ayudan mucho.
Continue

Listas enlazadas en C++: entendiendo la estructura sin morir en el intento

Listas enlazadas en C++: entendiendo la estructura sin morir en el intento

Resumen: En este post te explico qué es una lista enlazada en C++, por qué existe si ya hay arrays, y cómo implementar una lista enlazada simple paso a paso con ejemplos de código.

Nivel: Junior

Introducción

Si estás empezando con C++ o estructuras de datos, probablemente ya te preguntaste:
“Si tengo arrays y std::vector, ¿para qué quiero una lista enlazada?”.
Buena pregunta.

Las listas enlazadas son una estructura de datos clásica que aparece en entrevistas, exámenes y, sobre todo,
te obliga a entender mejor cómo funcionan los punteros y la memoria dinámica en C++.
En este post vamos a ver:

  • Qué es una lista enlazada y cómo se diferencia de un array.
  • Cuándo tiene sentido usarla (y cuándo es mejor no complicarse).
  • Una implementación básica en C++ con las operaciones típicas.
  • Errores comunes que comete todo el mundo la primera vez (yo incluido).

¿Qué es una lista enlazada?

Una lista enlazada (singly linked list) es una colección de nodos donde:

  • Cada nodo guarda un valor.
  • Cada nodo sabe quién es el siguiente nodo (puntero next).
  • La lista solo conoce el primer nodo, llamado head.

Visualmente, algo así:


[valor1 | *] → [valor2 | *] → [valor3 | *] → nullptr

A diferencia de un array:

  • Los elementos no están uno al lado del otro en memoria.
  • Para acceder al elemento N, tienes que recorrer desde el inicio.
  • Agregar o eliminar elementos al inicio puede ser muy barato (O(1)).

¿Cuándo usar listas enlazadas y cuándo no?

Te conviene usar una lista enlazada cuando:

  • Necesitas hacer muchas inserciones y eliminaciones al inicio de la colección.
  • No te importa tanto el acceso “dame el índice 10” rápido.
  • Estás aprendiendo estructuras de datos y quieres entender bien punteros y memoria dinámica.

No es la mejor idea cuando:

  • Vas a acceder constantemente por índice (tipo v[100000]).
  • Tu caso de uso es básicamente una lista que solo crece y rara vez eliminas en medio.
  • Ya puedes usar std::vector o std::list de la STL sin reinventar la rueda.

En la práctica, en C++ del mundo real, normalmente usarás std::vector y compañía.
Pero entender listas enlazadas te hace mucho mejor programador.

Implementación básica de una lista enlazada en C++

Vamos con una implementación simple de una lista enlazada de enteros.

Definiendo el nodo


#include <iostream>

struct Node {
    int data;      // valor que guarda el nodo
    Node* next;    // puntero al siguiente nodo

    // Constructor cómodo
    Node(int value) : data(value), next(nullptr) {}
};

Manejando la lista

Podemos manejar la lista con un puntero head que apunte al primer nodo:


struct LinkedList {
    Node* head;

    LinkedList() : head(nullptr) {}

    // Insertar al inicio
    void push_front(int value) {
        Node* newNode = new Node(value);
        newNode->next = head;
        head = newNode;
    }

    // Mostrar todos los elementos
    void print() const {
        Node* current = head;
        while (current != nullptr) {
            std::cout << current->data << " -> ";
            current = current->next;
        }
        std::cout << "nullptr" << std::endl;
    }

    // Liberar memoria (destructor)
    ~LinkedList() {
        Node* current = head;
        while (current != nullptr) {
            Node* nextNode = current->next;
            delete current;
            current = nextNode;
        }
    }
};

Usando la lista en main


int main() {
    LinkedList list;

    list.push_front(10);
    list.push_front(20);
    list.push_front(30);

    list.print(); // 30 -> 20 -> 10 -> nullptr

    return 0;
}

Aquí estamos insertando siempre al inicio, así que el orden queda invertido:
el último que insertas es el primero de la lista.

Operaciones típicas en una lista enlazada

Insertar al final

Insertar al final requiere recorrer la lista hasta llegar al último nodo.


void push_back(int value) {
    Node* newNode = new Node(value);

    if (head == nullptr) {
        head = newNode;
        return;
    }

    Node* current = head;
    while (current->next != nullptr) {
        current = current->next;
    }
    current->next = newNode;
}

Eliminar el primer nodo con un cierto valor


bool remove(int value) {
    if (head == nullptr) return false;

    // Si el que hay que borrar es el primero
    if (head->data == value) {
        Node* temp = head;
        head = head->next;
        delete temp;
        return true;
    }

    Node* current = head;
    while (current->next != nullptr && current->next->data != value) {
        current = current->next;
    }

    if (current->next == nullptr) {
        // No se encontró el valor
        return false;
    }

    Node* nodeToDelete = current->next;
    current->next = current->next->next;
    delete nodeToDelete;
    return true;
}

Buscar un valor


bool contains(int value) const {
    Node* current = head;
    while (current != nullptr) {
        if (current->data == value) {
            return true;
        }
        current = current->next;
    }
    return false;
}

Ejemplo completo


#include <iostream>

struct Node {
    int data;
    Node* next;

    Node(int value) : data(value), next(nullptr) {}
};

struct LinkedList {
    Node* head;

    LinkedList() : head(nullptr) {}

    void push_front(int value) {
        Node* newNode = new Node(value);
        newNode->next = head;
        head = newNode;
    }

    void push_back(int value) {
        Node* newNode = new Node(value);

        if (head == nullptr) {
            head = newNode;
            return;
        }

        Node* current = head;
        while (current->next != nullptr) {
            current = current->next;
        }
        current->next = newNode;
    }

    bool remove(int value) {
        if (head == nullptr) return false;

        if (head->data == value) {
            Node* temp = head;
            head = head->next;
            delete temp;
            return true;
        }

        Node* current = head;
        while (current->next != nullptr && current->next->data != value) {
            current = current->next;
        }

        if (current->next == nullptr) {
            return false;
        }

        Node* nodeToDelete = current->next;
        current->next = current->next->next;
        delete nodeToDelete;
        return true;
    }

    bool contains(int value) const {
        Node* current = head;
        while (current != nullptr) {
            if (current->data == value) {
                return true;
            }
            current = current->next;
        }
        return false;
    }

    void print() const {
        Node* current = head;
        while (current != nullptr) {
            std::cout << current->data << " -> ";
            current = current->next;
        }
        std::cout << "nullptr" << std::endl;
    }

    ~LinkedList() {
        Node* current = head;
        while (current != nullptr) {
            Node* nextNode = current->next;
            delete current;
            current = nextNode;
        }
    }
};

int main() {
    LinkedList list;

    list.push_front(10);
    list.push_front(20);
    list.push_back(5);

    list.print(); // 20 -> 10 -> 5 -> nullptr

    list.remove(10);
    list.print(); // 20 -> 5 -> nullptr

    std::cout << "Contiene 5? " << (list.contains(5) ? "sí" : "no") << std::endl;
    std::cout << "Contiene 42? " << (list.contains(42) ? "sí" : "no") << std::endl;

    return 0;
}

Errores típicos con listas enlazadas (y cómo evitarlos)

  • Olvidar inicializar punteros: deja siempre next como nullptr en el constructor.
  • Acceder a current->next sin comprobar current != nullptr:
    esto es receta segura para un segmentation fault.
  • Fugas de memoria: cada new necesita un delete.
    Por eso el destructor que recorre y libera la lista es tan importante.
  • Perder la referencia a la lista: si reasignas head sin guardar el nodo anterior,
    te quedas sin acceso al resto de nodos.

Conclusión

Las listas enlazadas son una de esas estructuras que, al principio, parecen más complicadas de lo que “valen”.
Pero una vez las entiendes, te abren la puerta a muchas otras estructuras más avanzadas
(listas dobles, colas, pilas, árboles, grafos…).

En C++ moderno, en la mayoría de casos reales usarás contenedores de la STL
como std::vector o std::list.
Aún así, implementar tu propia lista enlazada te obliga a entender punteros, memoria dinámica y destructores,
lo cual es oro puro si quieres crecer como desarrollador C++.

Si te gustaría, en otro post podemos ver:

  • Listas doblemente enlazadas.
  • Implementar una pila o una cola usando una lista enlazada.
  • La misma estructura pero usando templates para soportar cualquier tipo, no solo int.

Recursos recomendados

  • Búsqueda en Google de “singly linked list C++” con imágenes: ayuda un montón a visualizar.
  • Documentación de std::list en C++ para ver cómo lo hace la STL.