50 Preguntas de Entrevista Técnica para Desarrolladores Full Stack (con Respuestas)
Devs

50 Preguntas de Entrevista Técnica para Desarrolladores Full Stack (con Respuestas)

calendar_today 24 Jan, 2026
schedule 31 min de lectura

50 Preguntas de Entrevista Técnica para Desarrolladores Full Stack (con Respuestas)

Las entrevistas técnicas para posiciones Full Stack pueden ser intimidantes. Los reclutadores evalúan tanto tus conocimientos de frontend como backend, además de tu capacidad para integrar ambos mundos. Esta guía contiene las 50 preguntas más frecuentes en entrevistas Full Stack, organizadas por categoría, con respuestas detalladas que te ayudarán a prepararte con confianza.

JavaScript y Fundamentos de Programación

1. ¿Cuál es la diferencia entre var, let y const?

Respuesta:

  • var: Tiene scope de función, puede ser redeclarada y reasignada. Sufre de hoisting (se eleva al inicio del scope).
  • let: Tiene scope de bloque, no puede ser redeclarada pero sí reasignada. También sufre hoisting pero con "temporal dead zone".
  • const: Tiene scope de bloque, no puede ser redeclarada ni reasignada (aunque sus propiedades sí pueden modificarse si es un objeto). Requiere inicialización al momento de declararse.

Ejemplo:



javascript

var x = 1;
var x = 2; // ✓ Funciona

let y = 1;
let y = 2; // ✗ Error: y ya fue declarada

const z = 1;
z = 2; // ✗ Error: no se puede reasignar

const obj = { name: 'Ana' };
obj.name = 'Carlos'; // ✓ Funciona (modificas la propiedad, no la referencia)

2. ¿Qué es el hoisting en JavaScript?

Respuesta:

Hoisting es el comportamiento de JavaScript donde las declaraciones de variables y funciones se "mueven" al inicio de su scope antes de la ejecución del código. Solo se elevan las declaraciones, no las inicializaciones.



javascript

console.log(nombre); // undefined (no error)
var nombre = 'Juan';

console.log(apellido); // ReferenceError
let apellido = 'Pérez';

saludar(); // "Hola" - funciona porque las funciones declaradas se elevan completamente
function saludar() {
  console.log('Hola');
}

3. Explica los conceptos de closures en JavaScript

Respuesta:

Un closure es una función que tiene acceso a variables de su scope exterior, incluso después de que la función exterior haya terminado de ejecutarse. Los closures "recuerdan" el entorno en el que fueron creados.



javascript

function crearContador() {
  let contador = 0;
  
  return function() {
    contador++;
    return contador;
  };
}

const miContador = crearContador();
console.log(miContador()); // 1
console.log(miContador()); // 2
console.log(miContador()); // 3

Usos prácticos: Encapsulación de datos, factory functions, callbacks, event handlers.

4. ¿Cuál es la diferencia entre == y ===?

Respuesta:

  • == (igualdad débil): Compara valores después de hacer coerción de tipos (conversión automática).
  • === (igualdad estricta): Compara valores Y tipos sin hacer conversión.



javascript

5 == '5'   // true (convierte '5' a número)
5 === '5'  // false (tipos diferentes)

null == undefined  // true
null === undefined // false

0 == false   // true
0 === false  // false

Mejor práctica: Usa siempre === a menos que específicamente necesites coerción de tipos.

5. ¿Qué es el Event Loop en JavaScript?

Respuesta:

El Event Loop es el mecanismo que permite a JavaScript ejecutar código asíncrono a pesar de ser single-threaded. Funciona así:

  1. Call Stack: Ejecuta código síncronamente
  2. Web APIs: Manejan operaciones asíncronas (setTimeout, fetch, etc.)
  3. Callback Queue: Cola de callbacks listos para ejecutarse
  4. Event Loop: Revisa si el Call Stack está vacío y mueve callbacks desde la Queue al Stack



javascript

console.log('1');

setTimeout(() => {
  console.log('2');
}, 0);

Promise.resolve().then(() => {
  console.log('3');
});

console.log('4');

// Resultado: 1, 4, 3, 2
// (Las promesas tienen prioridad sobre setTimeout en la Microtask Queue)

6. Explica this en JavaScript

Respuesta:

this se refiere al objeto que está ejecutando la función actual. Su valor depende del contexto de ejecución:



javascript

// En objeto
const persona = {
  nombre: 'Ana',
  saludar() {
    console.log(this.nombre); // 'Ana'
  }
};

// Arrow functions NO tienen su propio this
const persona2 = {
  nombre: 'Carlos',
  saludar: () => {
    console.log(this.nombre); // undefined (this del scope padre)
  }
};

// Con bind/call/apply puedes cambiar el this
function mostrarNombre() {
  console.log(this.nombre);
}

const usuario = { nombre: 'María' };
mostrarNombre.call(usuario); // 'María'

7. ¿Qué son las Promises y cómo funcionan?

Respuesta:

Una Promise es un objeto que representa el resultado eventual (éxito o fallo) de una operación asíncrona. Tiene tres estados:

  • Pending: Estado inicial
  • Fulfilled: Operación completada exitosamente
  • Rejected: Operación falló



javascript

const miPromesa = new Promise((resolve, reject) => {
  setTimeout(() => {
    const exito = true;
    if (exito) {
      resolve('Datos obtenidos');
    } else {
      reject('Error al obtener datos');
    }
  }, 1000);
});

miPromesa
  .then(resultado => console.log(resultado))
  .catch(error => console.error(error))
  .finally(() => console.log('Operación finalizada'));

8. ¿Cuál es la diferencia entre .map(), .forEach(), .filter() y .reduce()?

Respuesta:



javascript

const numeros = [1, 2, 3, 4, 5];

// forEach: Itera sin retornar nada
numeros.forEach(n => console.log(n)); // undefined

// map: Transforma cada elemento y retorna nuevo array
const dobles = numeros.map(n => n * 2); // [2, 4, 6, 8, 10]

// filter: Retorna nuevo array con elementos que cumplen condición
const pares = numeros.filter(n => n % 2 === 0); // [2, 4]

// reduce: Reduce array a un solo valor
const suma = numeros.reduce((acc, n) => acc + n, 0); // 15

9. Explica async/await

Respuesta:

async/await es sintaxis moderna para trabajar con Promises de forma más legible, parecida a código síncrono.



javascript

// Con Promises
function obtenerUsuario() {
  return fetch('/api/user')
    .then(res => res.json())
    .then(data => console.log(data))
    .catch(err => console.error(err));
}

// Con async/await
async function obtenerUsuario() {
  try {
    const res = await fetch('/api/user');
    const data = await res.json();
    console.log(data);
  } catch (err) {
    console.error(err);
  }
}

Reglas:

  • Solo puedes usar await dentro de funciones async
  • async siempre retorna una Promise
  • Usa try/catch para manejar errores

10. ¿Qué es el spread operator y rest parameters?

Respuesta:

Spread operator (...): Expande elementos de un iterable



javascript

const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]

const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }

Rest parameters: Agrupa argumentos en un array



javascript

function sumar(...numeros) {
  return numeros.reduce((acc, n) => acc + n, 0);
}

sumar(1, 2, 3, 4); // 10

React y Frontend

11. ¿Qué son los componentes en React?

Respuesta:

Los componentes son piezas reutilizables e independientes de UI. Hay dos tipos:

Funcionales (recomendados):



javascript

function Saludo({ nombre }) {
  return <h1>Hola, {nombre}</h1>;
}

De clase (legacy):



javascript

class Saludo extends React.Component {
  render() {
    return <h1>Hola, {this.props.nombre}</h1>;
  }
}

Características:

  • Reciben props (datos de entrada)
  • Retornan JSX (elementos React)
  • Pueden manejar estado local
  • Deben tener nombres en PascalCase

12. ¿Qué es el Virtual DOM?

Respuesta:

El Virtual DOM es una representación en memoria del DOM real. React usa un algoritmo de "reconciliación" para comparar versiones:

  1. Se crea un árbol virtual del componente
  2. Cuando hay cambios, se crea un nuevo árbol virtual
  3. React compara ambos árboles (diffing)
  4. Solo actualiza en el DOM real lo que cambió

Ventajas:

  • Mejor performance (batch updates)
  • Menos manipulaciones directas del DOM
  • Permite rendering declarativo

13. Explica useState y useEffect

Respuesta:

useState: Hook para agregar estado local a componentes funcionales



javascript

function Contador() {
  const [count, setCount] = useState(0);
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Clicks: {count}
    </button>
  );
}

useEffect: Hook para efectos secundarios (API calls, subscripciones, DOM)



javascript

function Usuario({ id }) {
  const [usuario, setUsuario] = useState(null);
  
  useEffect(() => {
    // Se ejecuta después del render
    fetch(`/api/users/${id}`)
      .then(res => res.json())
      .then(data => setUsuario(data));
    
    // Cleanup function (opcional)
    return () => {
      console.log('Componente desmontado');
    };
  }, [id]); // Array de dependencias
  
  return <div>{usuario?.nombre}</div>;
}

14. ¿Cuál es la diferencia entre props y state?

Respuesta:

PropsStateDatos que recibe el componenteDatos internos del componenteInmutables desde el componente hijoMutable con setState/useStateVienen del componente padreSe inicializa en el componenteUsadas para comunicación descendenteUsado para interactividad local



javascript

// Props
function Hijo({ mensaje }) {
  // mensaje es inmutable aquí
  return <p>{mensaje}</p>;
}

function Padre() {
  return <Hijo mensaje="Hola desde padre" />;
}

// State
function Contador() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}

15. ¿Qué es el prop drilling y cómo lo evitas?

Respuesta:

Prop drilling es pasar props a través de múltiples niveles de componentes que no las usan, solo para llegar al componente final.

Problema:



javascript

function App() {
  const [user, setUser] = useState({ name: 'Ana' });
  return <Nivel1 user={user} />;
}

function Nivel1({ user }) {
  return <Nivel2 user={user} />; // solo pasa la prop
}

function Nivel2({ user }) {
  return <Nivel3 user={user} />; // solo pasa la prop
}

function Nivel3({ user }) {
  return <h1>{user.name}</h1>; // finalmente la usa
}

Soluciones:

  1. Context API:



javascript

const UserContext = React.createContext();

function App() {
  const [user, setUser] = useState({ name: 'Ana' });
  return (
    <UserContext.Provider value={user}>
      <Nivel1 />
    </UserContext.Provider>
  );
}

function Nivel3() {
  const user = useContext(UserContext);
  return <h1>{user.name}</h1>;
}
  1. State management (Redux, Zustand)
  2. Component composition

16. ¿Qué son los React Hooks más comunes?

Respuesta:

useState: Estado local



javascript

const [state, setState] = useState(initialValue);

useEffect: Efectos secundarios



javascript

useEffect(() => { /* efecto */ }, [dependencies]);

useContext: Consumir contexto



javascript

const value = useContext(MyContext);

useRef: Referencia mutable que persiste entre renders



javascript

const inputRef = useRef(null);
<input ref={inputRef} />

useMemo: Memoiza valores computados



javascript

const valorCostoso = useMemo(() => calcularAlgo(data), [data]);

useCallback: Memoiza funciones



javascript

const handleClick = useCallback(() => {
  console.log(count);
}, [count]);

useReducer: Estado complejo con lógica tipo Redux



javascript

const [state, dispatch] = useReducer(reducer, initialState);

17. Explica el ciclo de vida de un componente React

Respuesta:

En componentes funcionales con hooks:



javascript

function Componente() {
  // Montaje: Se ejecuta una vez
  useEffect(() => {
    console.log('Componente montado');
    
    // Desmontaje: Cleanup
    return () => {
      console.log('Componente desmontado');
    };
  }, []); // Array vacío = solo al montar
  
  // Actualización: Cada vez que cambia 'count'
  useEffect(() => {
    console.log('Count cambió');
  }, [count]);
  
  // Cada render
  useEffect(() => {
    console.log('Componente renderizado');
  }); // Sin array de dependencias
}

Equivalencias con clases:

  • useEffect(() => {}, [])componentDidMount
  • useEffect(() => {})componentDidUpdate
  • useEffect(() => { return () => {} }, [])componentWillUnmount

18. ¿Qué es el controlled vs uncontrolled components?

Respuesta:

Controlled Component: React controla el valor del input



javascript

function FormControlado() {
  const [valor, setValor] = useState('');
  
  return (
    <input 
      value={valor}
      onChange={(e) => setValor(e.target.value)}
    />
  );
}

Uncontrolled Component: DOM controla el valor



javascript

function FormNoControlado() {
  const inputRef = useRef();
  
  const handleSubmit = () => {
    console.log(inputRef.current.value);
  };
  
  return <input ref={inputRef} />;
}

Recomendación: Usa controlled components para formularios con validación en tiempo real.

19. ¿Qué es Redux y cuándo lo usarías?

Respuesta:

Redux es una librería de state management basada en tres principios:

  1. Single source of truth: Un solo store
  2. State is read-only: Solo se modifica con actions
  3. Changes with pure functions: Reducers puros



javascript

// Action
const incrementar = () => ({ type: 'INCREMENT' });

// Reducer
function contadorReducer(state = { count: 0 }, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    default:
      return state;
  }
}

// Store
const store = createStore(contadorReducer);

// Dispatch
store.dispatch(incrementar());

Úsalo cuando:

  • Múltiples componentes necesitan el mismo estado
  • Estado se actualiza frecuentemente
  • Lógica de actualización es compleja
  • App mediana/grande con muchos estados compartidos

No lo uses si: Tu app es pequeña, Context API es suficiente.

20. ¿Qué es el key prop en listas de React?

Respuesta:

key es un atributo especial que ayuda a React a identificar qué elementos cambiaron, se agregaron o eliminaron en una lista.

Malo:



javascript

items.map((item, index) => (
  <li key={index}>{item.nombre}</li>
  // ❌ Usar index puede causar bugs si el orden cambia
));

Bueno:



javascript

items.map(item => (
  <li key={item.id}>{item.nombre}</li>
  // ✓ Usa un ID único y estable
));

Por qué importa:

  • Sin keys, React re-renderiza toda la lista
  • Con keys incorrectas, el estado puede quedar en el elemento equivocado
  • Keys deben ser únicas entre hermanos, no globalmente

Node.js y Backend

21. ¿Qué es Node.js y cómo funciona?

Respuesta:

Node.js es un runtime de JavaScript basado en el motor V8 de Chrome que permite ejecutar JS en el servidor.

Características clave:

  • Event-driven: Basado en eventos
  • Non-blocking I/O: Operaciones asíncronas
  • Single-threaded: Un hilo principal con event loop
  • NPM: Gestor de paquetes más grande del mundo

Ejemplo:



javascript

const http = require('http');

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hola desde Node.js');
});

server.listen(3000, () => {
  console.log('Servidor en puerto 3000');
});

Ventajas:

  • JavaScript en frontend y backend
  • Gran ecosistema (npm)
  • Excelente para I/O intensivo
  • Real-time applications (WebSockets)

22. Explica el patrón MVC en backend

Respuesta:

MVC (Model-View-Controller) separa la aplicación en tres componentes:

Model: Lógica de datos y base de datos



javascript

// models/User.js
const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  nombre: String,
  email: String
});

module.exports = mongoose.model('User', userSchema);

View: Presentación (en APIs, sería la respuesta JSON)



javascript

// En APIs REST, la "vista" es el JSON response
res.json({ success: true, data: usuarios });

Controller: Lógica de negocio



javascript

// controllers/userController.js
const User = require('../models/User');

exports.getUsers = async (req, res) => {
  try {
    const usuarios = await User.find();
    res.json(usuarios);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
};

Router: Conecta rutas con controllers



javascript

// routes/users.js
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');

router.get('/users', userController.getUsers);

module.exports = router;

23. ¿Qué es Express.js y por qué se usa?

Respuesta:

Express es un framework minimalista de Node.js para construir aplicaciones web y APIs.

Características:

  • Routing robusto
  • Middleware system
  • Template engines
  • Manejo de requests/responses simplificado



javascript

const express = require('express');
const app = express();

// Middleware
app.use(express.json());

// Rutas
app.get('/api/users', (req, res) => {
  res.json({ users: [] });
});

app.post('/api/users', (req, res) => {
  const nuevoUser = req.body;
  res.status(201).json(nuevoUser);
});

// Error handling
app.use((err, req, res, next) => {
  res.status(500).json({ error: err.message });
});

app.listen(3000);

Ventajas:

  • Ligero y flexible
  • Gran ecosistema de middleware
  • Curva de aprendizaje suave
  • Estándar de facto en Node.js

24. ¿Qué son los middlewares en Express?

Respuesta:

Los middlewares son funciones que tienen acceso al objeto request (req), response (res), y la siguiente función middleware (next) en el ciclo de request/response.



javascript

// Middleware de logging
const logger = (req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next(); // Pasa al siguiente middleware
};

// Middleware de autenticación
const auth = (req, res, next) => {
  const token = req.headers.authorization;
  if (!token) {
    return res.status(401).json({ error: 'No autorizado' });
  }
  next();
};

// Uso
app.use(logger); // Aplica a todas las rutas
app.get('/api/protected', auth, (req, res) => {
  res.json({ data: 'Datos protegidos' });
});

Tipos:

  • Application-level: app.use()
  • Router-level: router.use()
  • Error-handling: Con 4 parámetros (err, req, res, next)
  • Built-in: express.json(), express.static()
  • Third-party: cors, morgan, etc.

25. Explica REST API y sus principios

Respuesta:

REST (Representational State Transfer) es un estilo arquitectónico para diseñar APIs web.

Principios:

  1. Stateless: Cada request contiene toda la info necesaria
  2. Client-Server: Separación de responsabilidades
  3. Cacheable: Responses deben indicar si son cacheables
  4. Uniform Interface: Interfaz consistente

Métodos HTTP:



javascript

// GET - Obtener recursos
app.get('/api/posts', getPosts);
app.get('/api/posts/:id', getPostById);

// POST - Crear recurso
app.post('/api/posts', createPost);

// PUT - Actualizar completamente
app.put('/api/posts/:id', updatePost);

// PATCH - Actualizar parcialmente
app.patch('/api/posts/:id', partialUpdate);

// DELETE - Eliminar
app.delete('/api/posts/:id', deletePost);
```

**Códigos de respuesta comunes:**
- 200: OK
- 201: Created
- 400: Bad Request
- 401: Unauthorized
- 404: Not Found
- 500: Internal Server Error

### 26. ¿Qué es JWT y cómo funciona la autenticación?

**Respuesta:**

JWT (JSON Web Token) es un estándar para transmitir información de forma segura entre partes como un objeto JSON.

**Estructura:**
```
header.payload.signature

Implementación:



javascript

const jwt = require('jsonwebtoken');

// Generar token
const generarToken = (usuario) => {
  return jwt.sign(
    { id: usuario.id, email: usuario.email },
    process.env.JWT_SECRET,
    { expiresIn: '24h' }
  );
};

// Login
app.post('/api/login', async (req, res) => {
  const { email, password } = req.body;
  
  const usuario = await User.findOne({ email });
  if (!usuario) {
    return res.status(401).json({ error: 'Credenciales inválidas' });
  }
  
  const passwordValido = await bcrypt.compare(password, usuario.password);
  if (!passwordValido) {
    return res.status(401).json({ error: 'Credenciales inválidas' });
  }
  
  const token = generarToken(usuario);
  res.json({ token });
});

// Middleware de verificación
const verificarToken = (req, res, next) => {
  const token = req.headers.authorization?.split(' ')[1];
  
  if (!token) {
    return res.status(401).json({ error: 'Token no proporcionado' });
  }
  
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.usuario = decoded;
    next();
  } catch (error) {
    res.status(401).json({ error: 'Token inválido' });
  }
};

27. ¿Cómo manejas errores en Node.js/Express?

Respuesta:

1. Try-catch en async/await:



javascript

app.get('/api/users/:id', async (req, res) => {
  try {
    const usuario = await User.findById(req.params.id);
    if (!usuario) {
      return res.status(404).json({ error: 'Usuario no encontrado' });
    }
    res.json(usuario);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

2. Middleware de manejo de errores global:



javascript

// Middleware de error (siempre al final)
app.use((err, req, res, next) => {
  console.error(err.stack);
  
  res.status(err.status || 500).json({
    error: {
      message: err.message,
      ...(process.env.NODE_ENV === 'development' && { stack: err.stack })
    }
  });
});

3. Errores personalizados:



javascript

class ErrorPersonalizado extends Error {
  constructor(message, status) {
    super(message);
    this.status = status;
  }
}

// Uso
if (!usuario) {
  throw new ErrorPersonalizado('Usuario no encontrado', 404);
}

4. Async error wrapper:



javascript

const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

app.get('/api/users', asyncHandler(async (req, res) => {
  const usuarios = await User.find();
  res.json(usuarios);
}));
```

### 28. ¿Qué es CORS y cómo lo manejas?

**Respuesta:**

CORS (Cross-Origin Resource Sharing) es un mecanismo de seguridad que permite o restringe recursos solicitados desde otro dominio.

**Problema:**
```
Frontend en http://localhost:3000
Backend en http://localhost:5000
→ CORS error

Solución con el paquete cors:



javascript

const cors = require('cors');

// Permitir todos los orígenes (solo desarrollo)
app.use(cors());

// Configuración específica (producción)
app.use(cors({
  origin: 'https://miapp.com',
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization']
}));

// Múltiples orígenes
const whitelist = ['https://miapp.com', 'https://admin.miapp.com'];
app.use(cors({
  origin: (origin, callback) => {
    if (whitelist.indexOf(origin) !== -1 || !origin) {
      callback(null, true);
    } else {
      callback(new Error('No permitido por CORS'));
    }
  }
}));

29. Explica la diferencia entre SQL y NoSQL

Respuesta:

SQL (Relacional):

  • Estructura: Tablas con filas y columnas
  • Schema: Fijo y predefinido
  • Relaciones: Foreign keys, JOINs
  • Escalabilidad: Vertical (más potencia al servidor)
  • Ejemplos: MySQL, PostgreSQL, SQL Server



sql

-- Ejemplo SQL
SELECT u.nombre, p.titulo
FROM usuarios u
JOIN posts p ON u.id = p.user_id
WHERE u.activo = true;

NoSQL (No relacional):

  • Estructura: Documentos, key-value, grafos, columnas
  • Schema: Flexible o dinámico
  • Relaciones: Embebidas o referencias
  • Escalabilidad: Horizontal (más servidores)
  • Ejemplos: MongoDB, Redis, Cassandra



javascript

// Ejemplo MongoDB
db.usuarios.find({ activo: true });

Cuándo usar cada uno:

SQL:

  • Datos estructurados con relaciones complejas
  • Transacciones ACID críticas
  • Reporting complejo
  • Esquema estable

NoSQL:

  • Datos no estructurados o semi-estructurados
  • Escalabilidad masiva
  • Desarrollo ágil (schema flexible)
  • Caché, sesiones, real-time

30. ¿Cómo optimizas queries en bases de datos?

Respuesta:

1. Índices:



javascript

// MongoDB
userSchema.index({ email: 1 }); // Índice ascendente
userSchema.index({ nombre: 1, apellido: 1 }); // Compuesto

2. Selección de campos:



javascript

// ❌ Malo: Trae todos los campos
User.find();

// ✓ Bueno: Solo los campos necesarios
User.find().select('nombre email');

3. Paginación:



javascript

const page = req.query.page || 1;
const limit = 10;
const skip = (page - 1) * limit;

const usuarios = await User.find()
  .limit(limit)
  .skip(skip);

4. Populate eficiente:



javascript

// ❌ Malo: Trae todos los campos de posts
User.findById(id).populate('posts');

// ✓ Bueno: Solo campos necesarios
User.findById(id).populate('posts', 'titulo fecha');

5. Aggregation pipeline:



javascript

User.aggregate([
  { $match: { activo: true } },
  { $group: { _id: '$ciudad', total: { $sum: 1 } } },
  { $sort: { total: -1 } }
]);

6. Caché:



javascript

const redis = require('redis');
const client = redis.createClient();

// Guardar en caché
app.get('/api/users', async (req, res) => {
  const cached = await client.get('users');
  
  if (cached) {
    return res.json(JSON.parse(cached));
  }
  
  const usuarios = await User.find();
  await client.setEx('users', 3600, JSON.stringify(usuarios));
  
  res.json(usuarios);
});

Bases de Datos

31. Explica las relaciones en bases de datos

Respuesta:

One-to-One (1:1): Un usuario tiene un perfil



javascript

// SQL
CREATE TABLE usuarios (id, nombre);
CREATE TABLE perfiles (id, usuario_id UNIQUE, bio);

// MongoDB (embebido)
{
  nombre: 'Ana',
  perfil: { bio: '...', avatar: '...' }
}

One-to-Many (1:N): Un usuario tiene muchos posts



javascript

// SQL
CREATE TABLE usuarios (id, nombre);
CREATE TABLE posts (id, user_id, titulo);

// MongoDB (referencia)
{
  _id: 'user1',
  nombre: 'Ana'
}
{
  _id: 'post1',
  user_id: 'user1',
  titulo: 'Mi post'
}

Many-to-Many (N:M): Estudiantes y cursos



javascript

// SQL (tabla intermedia)
CREATE TABLE estudiantes (id, nombre);
CREATE TABLE cursos (id, nombre);
CREATE TABLE inscripciones (estudiante_id, curso_id);

// MongoDB
{
  _id: 'student1',
  nombre: 'Juan',
  cursos: ['curso1', 'curso2'] // Array de referencias
}
```

### 32. ¿Qué es normalización de bases de datos?

**Respuesta:**

Normalización es organizar datos para reducir redundancia y mejorar integridad.

**Formas normales:**

**1NF (Primera Forma Normal):**
- Valores atómicos (no arrays en celdas)
- Cada columna tiene un tipo de dato único
```
❌ usuarios: id, nombre, telefonos="123,456,789"
✓ usuarios: id, nombre
✓ telefonos: id, usuario_id, numero

2NF (Segunda Forma Normal):

  • Cumple 1NF
  • No hay dependencias parciales de la clave

3NF (Tercera Forma Normal):

  • Cumple 2NF
  • No hay dependencias transitivas

Ejemplo:



sql

-- ❌ Desnormalizado (redundancia)
CREATE TABLE pedidos (
  id INT,
  producto VARCHAR(100),
  categoria VARCHAR(50),
  precio DECIMAL,
  cliente VARCHAR(100),
  email_cliente VARCHAR(100)
);

-- ✓ Normalizado
CREATE TABLE clientes (id, nombre, email);
CREATE TABLE productos (id, nombre, categoria, precio);
CREATE TABLE pedidos (id, cliente_id, producto_id, fecha);

Cuándo desnormalizar:

  • Reads muy frecuentes vs writes raros
  • Performance crítica
  • Data warehouse / reporting

33. ¿Qué son las transacciones ACID?

Respuesta:

ACID garantiza confiabilidad en transacciones de bases de datos:

Atomicity (Atomicidad): Todo o nada - si falla una parte, se revierte todo



javascript

// MongoDB
const session = await mongoose.startSession();
session.startTransaction();

try {
  await User.updateOne({ _id: userId }, { $inc: { balance: -100 } }, { session });
  await User.updateOne({ _id: receiverId }, { $inc: { balance: 100 } }, { session });
  
  await session.commitTransaction();
} catch (error) {
  await session.abortTransaction();
  throw error;
} finally {
  session.endSession();
}

Consistency (Consistencia): Los datos deben cumplir reglas de validación

Isolation (Aislamiento): Transacciones concurrentes no interfieren entre sí

Durability (Durabilidad): Datos confirmados persisten aunque falle el sistema

34. Explica índices en bases de datos

Respuesta:

Los índices son estructuras de datos que mejoran la velocidad de búsqueda.

Tipos:

Single field:



javascript

db.users.createIndex({ email: 1 });

Compound (múltiples campos):



javascript

db.products.createIndex({ category: 1, price: -1 });

Unique:



javascript

db.users.createIndex({ email: 1 }, { unique: true });

Text (búsqueda de texto):



javascript

db.posts.createIndex({ titulo: 'text', contenido: 'text' });

Ventajas:

  • Queries más rápidos (especialmente en tablas grandes)
  • Enforce uniqueness
  • Sorting eficiente

Desventajas:

  • Consume espacio en disco
  • Ralentiza INSERT, UPDATE, DELETE
  • Demasiados índices pueden ser contraproducentes

Best practices:

  • Indexa campos usados frecuentemente en WHERE, JOIN, ORDER BY
  • Usa EXPLAIN para analizar queries
  • Evita indexar campos con baja cardinalidad (ej: género)

35. ¿Qué es un ORM y por qué usarlo?

Respuesta:

ORM (Object-Relational Mapping) mapea objetos de código a tablas de base de datos.

Ejemplos populares:

  • Sequelize (SQL en Node.js)
  • Mongoose (MongoDB en Node.js)
  • TypeORM (TypeScript)
  • Prisma (moderno, type-safe)

Mongoose ejemplo:



javascript

const userSchema = new mongoose.Schema({
  nombre: { type: String, required: true },
  email: { type: String, unique: true },
  edad: { type: Number, min: 18 }
});

const User = mongoose.model('User', userSchema);

// Crear
const usuario = await User.create({ nombre: 'Ana', email: 'ana@email.com' });

// Leer
const usuarios = await User.find({ edad: { $gte: 18 } });

// Actualizar
await User.updateOne({ _id: id }, { nombre: 'Ana García' });

// Eliminar
await User.deleteOne({ _id: id });

Ventajas:

  • Abstracción de la BD (cambias fácilmente entre SQL/NoSQL)
  • Validaciones built-in
  • Menos SQL manual
  • Type safety (con TypeScript)
  • Migrations automáticas

Desventajas:

  • Curva de aprendizaje
  • Queries complejas pueden ser difíciles
  • Overhead de performance (en algunos casos)

Seguridad

36. ¿Cómo proteges contra ataques comunes?

Respuesta:

SQL Injection:



javascript

// ❌ Vulnerable
const query = `SELECT * FROM users WHERE email = '${email}'`;

// ✓ Usa prepared statements
const query = 'SELECT * FROM users WHERE email = ?';
db.query(query, [email]);

// ✓ Con ORM
User.findOne({ email });

XSS (Cross-Site Scripting):



javascript

// Sanitiza inputs
const sanitizeHtml = require('sanitize-html');
const clean = sanitizeHtml(userInput);

// Escapa output en templates
<p>{escape(userContent)}</p>

// Content Security Policy headers
app.use(helmet());

CSRF (Cross-Site Request Forgery):



javascript

const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });

app.get('/form', csrfProtection, (req, res) => {
  res.render('form', { csrfToken: req.csrfToken() });
});

Rate Limiting:



javascript

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutos
  max: 100 // límite de requests
});

app.use('/api/', limiter);

Validación de inputs:



javascript

const { body, validationResult } = require('express-validator');

app.post('/register',
  body('email').isEmail(),
  body('password').isLength({ min: 8 }),
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    // Procesar
  }
);

37. ¿Cómo hasheas y validas contraseñas?

Respuesta:

NUNCA guardes contraseñas en texto plano. Usa bcrypt.



javascript

const bcrypt = require('bcrypt');

// Hashear contraseña (registro)
const hashPassword = async (password) => {
  const saltRounds = 10;
  return await bcrypt.hash(password, saltRounds);
};

// Registro de usuario
app.post('/register', async (req, res) => {
  const { email, password } = req.body;
  
  // Validar fuerza de contraseña
  if (password.length < 8) {
    return res.status(400).json({ error: 'Contraseña muy corta' });
  }
  
  const hashedPassword = await hashPassword(password);
  
  const usuario = await User.create({
    email,
    password: hashedPassword
  });
  
  res.status(201).json({ message: 'Usuario creado' });
});

// Login - validar contraseña
app.post('/login', async (req, res) => {
  const { email, password } = req.body;
  
  const usuario = await User.findOne({ email });
  if (!usuario) {
    return res.status(401).json({ error: 'Credenciales inválidas' });
  }
  
  const passwordValido = await bcrypt.compare(password, usuario.password);
  if (!passwordValido) {
    return res.status(401).json({ error: 'Credenciales inválidas' });
  }
  
  // Generar token
  const token = jwt.sign({ id: usuario.id }, process.env.JWT_SECRET);
  res.json({ token });
});

Best practices:

  • Salt rounds: 10-12 (más alto = más seguro pero más lento)
  • Nunca uses MD5 o SHA-1 para contraseñas
  • Implementa rate limiting en login
  • Mensajes de error genéricos ("Credenciales inválidas" en lugar de "Email no existe")

38. ¿Qué es HTTPS y por qué es importante?

Respuesta:

HTTPS (HTTP Secure) encripta la comunicación entre cliente y servidor usando SSL/TLS.

Por qué es crítico:

  • Protege datos sensibles (contraseñas, tarjetas)
  • Previene man-in-the-middle attacks
  • Autenticación del servidor
  • Requisito para PWAs, HTTP/2, algunas APIs

Implementación con Express:



javascript

const https = require('https');
const fs = require('fs');

const options = {
  key: fs.readFileSync('private-key.pem'),
  cert: fs.readFileSync('certificate.pem')
};

https.createServer(options, app).listen(443);

En producción:

  • Usa Let's Encrypt (certificados gratuitos)
  • Configura redirect HTTP → HTTPS
  • Usa HSTS headers
  • Configuración SSL/TLS moderna

39. Explica OAuth 2.0

Respuesta:

OAuth 2.0 es un protocolo de autorización que permite a apps de terceros acceder a recursos sin compartir contraseñas.

Flujo básico (Authorization Code):

  1. Usuario hace clic en "Login con Google"
  2. Redirige a Google con client_id y redirect_uri
  3. Usuario autoriza permisos
  4. Google redirige de vuelta con un código
  5. Tu servidor intercambia código por access token
  6. Usas token para acceder a recursos del usuario



javascript

const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;

passport.use(new GoogleStrategy({
  clientID: process.env.GOOGLE_CLIENT_ID,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET,
  callbackURL: '/auth/google/callback'
},
(accessToken, refreshToken, profile, done) => {
  // Buscar o crear usuario
  User.findOrCreate({ googleId: profile.id }, (err, user) => {
    return done(err, user);
  });
}));

// Rutas
app.get('/auth/google',
  passport.authenticate('google', { scope: ['profile', 'email'] })
);

app.get('/auth/google/callback',
  passport.authenticate('google', { failureRedirect: '/login' }),
  (req, res) => {
    res.redirect('/dashboard');
  }
);

Componentes:

  • Resource Owner: Usuario
  • Client: Tu aplicación
  • Authorization Server: Google, Facebook, etc.
  • Resource Server: API con los datos del usuario

40. ¿Qué son las variables de entorno?

Respuesta:

Variables de entorno almacenan configuración sensible fuera del código.

Uso con dotenv:



javascript

// .env (NUNCA commitear a git)
DATABASE_URL=mongodb://localhost:27017/miapp
JWT_SECRET=supersecretkey123
PORT=5000
NODE_ENV=development

// .gitignore
.env

// app.js
require('dotenv').config();

const dbUrl = process.env.DATABASE_URL;
const port = process.env.PORT || 3000;

mongoose.connect(process.env.DATABASE_URL);

app.listen(port, () => {
  console.log(`Servidor en puerto ${port}`);
});

Qué guardar en .env:

  • API keys
  • Database credentials
  • JWT secrets
  • Third-party service tokens
  • Configuración específica del entorno

Best practices:

  • Nunca hardcodees secretos en el código
  • Usa diferentes .env para dev/staging/prod
  • Documenta variables requeridas en .env.example
  • En producción, usa servicios como AWS Secrets Manager

Testing y Herramientas

41. ¿Qué tipos de testing conoces?

Respuesta:

Unit Testing: Prueba funciones/métodos individuales



javascript

// sum.test.js (Jest)
const sum = (a, b) => a + b;

test('suma 1 + 2 es 3', () => {
  expect(sum(1, 2)).toBe(3);
});

test('suma números negativos', () => {
  expect(sum(-1, -2)).toBe(-3);
});

Integration Testing: Prueba cómo interactúan componentes



javascript

// api.test.js
const request = require('supertest');
const app = require('../app');

describe('POST /api/users', () => {
  it('crea un usuario nuevo', async () => {
    const res = await request(app)
      .post('/api/users')
      .send({ nombre: 'Ana', email: 'ana@test.com' });
    
    expect(res.status).toBe(201);
    expect(res.body).toHaveProperty('id');
  });
});

E2E (End-to-End): Prueba flujos completos desde la perspectiva del usuario



javascript

// Cypress
describe('Login flow', () => {
  it('permite login con credenciales válidas', () => {
    cy.visit('/login');
    cy.get('input[name=email]').type('user@test.com');
    cy.get('input[name=password]').type('password123');
    cy.get('button[type=submit]').click();
    cy.url().should('include', '/dashboard');
  });
});
```

**Pirámide de testing:**
```
     /\
    /E2E\       Pocos, lentos, costosos
   /------\
  /Integra\    Moderados
 /----------\
/ Unit Tests \  Muchos, rápidos, baratos

42. Explica TDD (Test-Driven Development)

Respuesta:

TDD es escribir tests ANTES del código de producción.

Ciclo Red-Green-Refactor:

  1. Red: Escribe un test que falle



javascript

test('usuario puede registrarse', async () => {
  const response = await register('ana@test.com', 'pass123');
  expect(response.success).toBe(true);
});
// ❌ Falla porque register() no existe
  1. Green: Escribe el código mínimo para pasar el test



javascript

const register = async (email, password) => {
  return { success: true };
};
// ✓ Pasa
  1. Refactor: Mejora el código sin romper tests



javascript

const register = async (email, password) => {
  if (!email || !password) throw new Error('Faltan datos');
  const user = await User.create({ email, password: hash(password) });
  return { success: true, userId: user.id };
};

Beneficios:

  • Código más testeable
  • Menos bugs
  • Documentación viva
  • Confianza para refactorizar

43. ¿Qué es Git y comandos esenciales?

Respuesta:

Git es un sistema de control de versiones distribuido.

Comandos básicos:



bash

# Inicializar repo
git init

# Clonar proyecto
git clone https://github.com/user/repo.git

# Ver estado
git status

# Agregar cambios al staging
git add .                 # Todos los archivos
git add archivo.js        # Archivo específico

# Commit
git commit -m "Mensaje descriptivo"

# Ver historial
git log
git log --oneline

# Branches
git branch                # Ver branches
git branch feature-login  # Crear branch
git checkout feature-login # Cambiar a branch
git checkout -b new-branch # Crear y cambiar

# Merge
git checkout main
git merge feature-login

# Push/Pull
git push origin main
git pull origin main

# Deshacer cambios
git checkout -- archivo.js  # Descartar cambios
git reset HEAD archivo.js   # Quitar del staging
git revert commit_hash      # Revertir commit

Flujo Git típico:



bash

git checkout -b feature/nueva-funcionalidad
# ... hacer cambios ...
git add .
git commit -m "feat: agregar nueva funcionalidad"
git push origin feature/nueva-funcionalidad
# ... crear Pull Request en GitHub ...

44. ¿Qué es CI/CD?

Respuesta:

CI (Continuous Integration): Integrar cambios de código frecuentemente con tests automáticos

CD (Continuous Deployment/Delivery): Desplegar automáticamente a producción

GitHub Actions ejemplo:



yaml

# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v2
      
      - name: Setup Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm install
      
      - name: Run tests
        run: npm test
      
      - name: Build
        run: npm run build

Beneficios:

  • Detección temprana de bugs
  • Deployments más frecuentes y confiables
  • Menos trabajo manual
  • Feedback rápido a desarrolladores

45. ¿Qué es Docker?

Respuesta:

Docker empaqueta aplicaciones con todas sus dependencias en contenedores portables.

Dockerfile ejemplo:



dockerfile

# Imagen base
FROM node:18-alpine

# Directorio de trabajo
WORKDIR /app

# Copiar package.json
COPY package*.json ./

# Instalar dependencias
RUN npm install

# Copiar código
COPY . .

# Exponer puerto
EXPOSE 3000

# Comando de inicio
CMD ["npm", "start"]

docker-compose.yml:



yaml

version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=mongodb://mongo:27017/miapp
    depends_on:
      - mongo
  
  mongo:
    image: mongo:latest
    ports:
      - "27017:27017"
    volumes:
      - mongo-data:/data/db

volumes:
  mongo-data:

Comandos básicos:



bash

# Construir imagen
docker build -t miapp .

# Correr contenedor
docker run -p 3000:3000 miapp

# Ver contenedores
docker ps

# Con docker-compose
docker-compose up
docker-compose down

Ventajas:

  • Mismo entorno en dev, staging, prod
  • Fácil escalabilidad
  • Aislamiento de dependencias
  • Deploy consistente

Conceptos Avanzados

46. ¿Qué es el patrón Repository?

Respuesta:

Repository abstrae la lógica de acceso a datos, separándola de la lógica de negocio.



javascript

// repositories/userRepository.js
class UserRepository {
  async findAll() {
    return await User.find();
  }
  
  async findById(id) {
    return await User.findById(id);
  }
  
  async findByEmail(email) {
    return await User.findOne({ email });
  }
  
  async create(userData) {
    return await User.create(userData);
  }
  
  async update(id, userData) {
    return await User.findByIdAndUpdate(id, userData, { new: true });
  }
  
  async delete(id) {
    return await User.findByIdAndDelete(id);
  }
}

module.exports = new UserRepository();

// services/userService.js
const userRepository = require('../repositories/userRepository');

class UserService {
  async registerUser(email, password) {
    // Validaciones de negocio
    const existingUser = await userRepository.findByEmail(email);
    if (existingUser) {
      throw new Error('Email ya registrado');
    }
    
    const hashedPassword = await bcrypt.hash(password, 10);
    return await userRepository.create({ email, password: hashedPassword });
  }
}

// controllers/userController.js
exports.register = async (req, res) => {
  try {
    const usuario = await userService.registerUser(req.body.email, req.body.password);
    res.status(201).json(usuario);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
};

Benefijas:

  • Fácil de testear (puedes mockear el repository)
  • Cambiar la BD sin tocar lógica de negocio
  • Código más organizado y mantenible

47. ¿Qué es GraphQL?

Respuesta:

GraphQL es un lenguaje de consulta para APIs que permite al cliente pedir exactamente los datos que necesita.

Diferencias con REST:

REST:



javascript

GET /api/users/1
{
  "id": 1,
  "nombre": "Ana",
  "email": "ana@email.com",
  "telefono": "123456",
  "direccion": {...},
  "posts": [...]  // Quizás no necesitas esto
}

GraphQL:



graphql

query {
  user(id: 1) {
    nombre
    email
  }
}

# Respuesta: solo lo que pediste
{
  "data": {
    "user": {
      "nombre": "Ana",
      "email": "ana@email.com"
    }
  }
}

Implementación básica:



javascript

const { ApolloServer, gql } = require('apollo-server');

// Schema
const typeDefs = gql`
  type User {
    id: ID!
    nombre: String!
    email: String!
    posts: [Post]
  }
  
  type Post {
    id: ID!
    titulo: String!
    autor: User!
  }
  
  type Query {
    users: [User]
    user(id: ID!): User
  }
  
  type Mutation {
    createUser(nombre: String!, email: String!): User
  }
`;

// Resolvers
const resolvers = {
  Query: {
    users: () => User.find(),
    user: (_, { id }) => User.findById(id)
  },
  Mutation: {
    createUser: (_, { nombre, email }) => User.create({ nombre, email })
  },
  User: {
    posts: (user) => Post.find({ autorId: user.id })
  }
};

const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => console.log(`Server en ${url}`));
```

**Ventajas:**
- Un solo endpoint
- Cliente pide exactamente lo que necesita (no overfetching)
- Fuertemente tipado
- Introspection (documentación automática)

**Desventajas:**
- Curva de aprendizaje
- Caché más complejo que REST
- Puede ser overkill para APIs simples

### 48. ¿Qué es WebSocket y cuándo usarlo?

**Respuesta:**

WebSocket es un protocolo de comunicación bidireccional en tiempo real sobre una sola conexión TCP.

**HTTP vs WebSocket:**
```
HTTP: Cliente → Request → Servidor → Response
WebSocket: Cliente ↔ Conexión persistente ↔ Servidor

Implementación con Socket.io:



javascript

// Server
const io = require('socket.io')(server);

io.on('connection', (socket) => {
  console.log('Usuario conectado:', socket.id);
  
  // Escuchar eventos
  socket.on('mensaje', (data) => {
    console.log('Mensaje recibido:', data);
    
    // Emitir a todos
    io.emit('mensaje', data);
    
    // Emitir a todos excepto el emisor
    socket.broadcast.emit('mensaje', data);
  });
  
  socket.on('disconnect', () => {
    console.log('Usuario desconectado');
  });
});

// Client (React)
import io from 'socket.io-client';

const socket = io('http://localhost:3000');

useEffect(() => {
  socket.on('mensaje', (data) => {
    setMensajes(prev => [...prev, data]);
  });
  
  return () => socket.disconnect();
}, []);

const enviarMensaje = (texto) => {
  socket.emit('mensaje', { texto, autor: usuario });
};
```

**Casos de uso:**
- Chat en tiempo real
- Notificaciones push
- Juegos multijugador
- Dashboards con datos en vivo
- Colaboración en tiempo real (Google Docs)

**Cuándo NO usar:**
- Si HTTP polling es suficiente
- Datos que no cambian frecuentemente
- Necesitas caché agresivo

### 49. Explica microservicios vs monolito

**Respuesta:**

**Monolito:**
Una sola aplicación con toda la lógica
```
[Frontend + Backend + DB Logic] → DB
```

**Microservicios:**
Múltiples servicios independientes
```
Frontend → API Gateway → 
  ├ Auth Service → Auth DB
  ├ User Service → User DB
  ├ Product Service → Product DB
  └ Order Service → Order DB

Ventajas de microservicios:

  • Escalabilidad independiente
  • Tecnologías diferentes por servicio
  • Deploys independientes
  • Equipos autónomos
  • Fallas aisladas

Desventajas:

  • Complejidad operacional
  • Debugging más difícil
  • Latencia de red entre servicios
  • Transacciones distribuidas complejas

Cuándo usar monolito:

  • Startups/MVPs
  • Equipos pequeños
  • Dominio simple
  • Comenzar rápido

Cuándo considerar microservicios:

  • App madura que necesita escalar
  • Equipos grandes
  • Diferentes partes con requisitos distintos
  • Necesitas deploys independientes

50. ¿Qué es Serverless?

Respuesta:

Serverless (Functions as a Service) ejecuta código sin gestionar servidores.

AWS Lambda ejemplo:



javascript

// handler.js
exports.handler = async (event) => {
  const { nombre } = JSON.parse(event.body);
  
  return {
    statusCode: 200,
    body: JSON.stringify({ mensaje: `Hola, ${nombre}` })
  };
};

Características:

  • Paga solo por ejecución (no por servidor en espera)
  • Auto-escalado automático
  • No gestión de infraestructura
  • Stateless (sin estado entre invocaciones)

Casos de uso:

  • APIs con tráfico variable
  • Procesamiento de eventos (uploads, webhooks)
  • Scheduled tasks (cron jobs)
  • Backend para apps móviles

Limitaciones:

  • Cold starts (primera invocación lenta)
  • Tiempo de ejecución limitado
  • Vendor lock-in
  • Debugging complejo

Plataformas:

  • AWS Lambda
  • Google Cloud Functions
  • Azure Functions
  • Vercel Functions
  • Netlify Functions

Conclusión

Estas 50 preguntas cubren los conceptos fundamentales que todo desarrollador Full Stack debe dominar. La clave para entrevistas exitosas no es memorizar respuestas, sino entender los conceptos profundamente y poder explicarlos con claridad.

Tips finales para entrevistas:

  1. Practica en voz alta - Explica conceptos a un amigo o frente al espejo
  2. Construye proyectos - La teoría sin práctica no sirve
  3. Haz preguntas - Las entrevistas son bidireccionales
  4. Sé honesto - Si no sabes algo, dilo y muestra disposición a aprender
  5. Usa el método STAR - Situación, Tarea, Acción, Resultado para preguntas de comportamiento

Recursos para seguir preparándote:

  • LeetCode / HackerRank para algoritmos
  • Sistema de diseño (System Design Primer)
  • Documentación oficial de tecnologías
  • Proyectos personales en GitHub

Próximos pasos:

  • Revisa las preguntas que no dominaste
  • Implementa ejemplos de código tú mismo
  • Practica mock interviews con amigos o plataformas


#Devs #Desarrollador #Full Stack #Entrevistas