M7 — Git avanzado & semántica HTTP
Dos fundamentos que el mercado da por sentados y hunden entrevistas cuando fallan (ambos aparecieron como gap en pruebas técnicas reales).
Outcome del módulo: resolver un conflicto de merge sin romper nada, reescribir historia local con criterio, y diseñar una API que respete la semántica HTTP (métodos, status, idempotencia).
Concepto 1 — Git avanzado (rebase, cherry-pick, conflictos)
Idea núcleo: más allá de add/commit/push, un senior maneja historia: integra ramas limpio, mueve commits puntuales y resuelve conflictos con criterio.
Entender (capa 1)
Texto:
- merge vs rebase: merge preserva la historia real y crea un commit de merge; rebase reescribe tus commits sobre otra base → historia lineal, más limpia. Regla de oro: rebase solo ramas locales/no compartidas; nunca rebase algo ya pusheado que otros usan.
- cherry-pick: llevar UN commit específico a otra rama (ej: un hotfix) sin traer toda la rama.
- conflictos: cuando dos ramas tocan la misma línea; Git marca
<<<<<<< ======= >>>>>>>y vos elegís/combinás, despuésgit add+ continuar. - Extras senior:
git rebase -i(squash/reordenar),git bisect(buscar el commit que rompió),git reflog(recuperar lo “perdido”).
Visual:
flowchart TD
Q{¿qué necesito?} -->|integrar rama compartida| MERGE[merge · preserva historia]
Q -->|limpiar historia local antes de PR| REBASE[rebase · lineal, solo local]
Q -->|un commit puntual a otra rama| CP[cherry-pick]
Q -->|encontrar qué commit rompió| BISECT[git bisect]
Video: Git Merge vs Rebase Explained in 100 Seconds — Thetips4you
Ejemplo:
# resolver conflicto durante un rebase
git rebase main
# ... Git marca conflicto en archivo.py ...
# editás, elegís la versión correcta, luego:
git add archivo.py
git rebase --continue # (o git rebase --abort para cancelar todo)
# llevar solo un hotfix a otra rama
git cherry-pick <hash-del-commit>
# encontrar el commit que introdujo un bug
git bisect start; git bisect bad; git bisect good <hash-viejo>
Fijar (capa 2)
Nota atómica:
- Front: ¿cuándo NO se debe usar rebase?
- Back: nunca rebase una rama ya compartida/pusheada que otros usan — reescribe hashes y les rompe la historia. Rebase solo en ramas locales/propias antes de integrar. Para ramas compartidas: merge.
Feynman: “Merge es unir dos ríos y dejar ver dónde se juntaron. Rebase es reescribir tu diario para que parezca que siempre fuiste por el mismo camino — genial para tu diario privado, catástrofe si reescribís el diario compartido del equipo.”
Aplicar (capa 3)
Creá dos ramas que toquen la misma línea, provocá un conflicto y resolvelo. Después hacé un rebase -i para hacer squash de 3 commits en 1, y un cherry-pick de un commit a otra rama.
Límites
- Reescribir historia compartida = pecado capital; ante la duda, merge.
--forcesobre una rama compartida borra trabajo ajeno; usá--force-with-leasesi tenés que forzar tu propia rama.
Concepto 2 — Semántica HTTP
Idea núcleo: cada método y status code tiene un significado contractual. Respetarlo hace tu API predecible, cacheable y correcta; ignorarlo la vuelve un campo minado.
Entender (capa 1)
Texto:
- Métodos e idempotencia:
GET(leer, seguro, cacheable) ·POST(crear, no idempotente) ·PUT(reemplazar, idempotente) ·PATCH(modificar parcial) ·DELETE(idempotente). Idempotente = repetir la llamada da el mismo estado (clave para retries — cruza con M2 idempotencia). - Status codes: 2xx OK · 201 Created · 204 No Content · 3xx redirect · 400 bad request · 401 no autenticado · 403 sin permiso (cruza M4) · 404 no existe · 409 conflicto · 422 validación · 429 rate limit · 5xx error del server.
- Headers que importan:
Cache-Control/ETag(caching),Content-Type/Accept(negociación),Authorization,Idempotency-Key.
Visual:
flowchart LR
M[método] --> G[GET · seguro · cacheable]
M --> PO[POST · crea · NO idempotente]
M --> PU[PUT · reemplaza · idempotente]
M --> DE[DELETE · idempotente]
Video: HTTP Methods Explained | GET, POST, PUT, and Idempotency in Simple Terms — Leela Web Dev
Ejemplo (mal → bien):
# ❌ todo por POST y siempre 200: rompe caching, retries y clientes
@app.post("/get-user") # debería ser GET
def get_user(): return {"ok": True} # 200 hasta cuando falla
# ✅ semántica correcta
@app.get("/users/{id}") # GET para leer
def get_user(id):
u = db.get(id)
if u is None:
raise HTTPException(404) # no existe → 404, no 200
return u # 200 con el recurso
@app.post("/users", status_code=201) # crear → 201
def create_user(data): ...
Fijar (capa 2)
Nota atómica:
- Front: ¿qué métodos HTTP son idempotentes y por qué importa?
- Back: GET, PUT, DELETE (y HEAD/OPTIONS) son idempotentes: repetirlos deja el mismo estado. POST no. Importa porque las redes reintentan: un POST duplicado crea dos recursos (por eso idempotency keys), un PUT duplicado no hace daño.
Feynman: “PUT es el interruptor de la luz: lo aprietes una o cinco veces, queda en el estado que pediste. POST es agregar un plato a la mesa: cada vez que lo mandás, aparece otro plato.”
Aplicar (capa 3)
Tomá una API que usa POST para todo y devuelve 200 siempre; reescribila con métodos y status correctos (GET/POST/PUT/DELETE, 201/404/422). Verificá con curl -i que los status son correctos.
Límites
- No sobre-ingenierices status raros; un set claro (200/201/204/400/401/403/404/409/422/429/5xx) cubre casi todo.
- PATCH tiene semántica ambigua (merge vs JSON Patch); documentá cuál usás.
Checklist de dominio M7
Marcás cuando podés explicar (Feynman) + aplicar:
- merge vs rebase (y cuándo NO rebase)
- cherry-pick, rebase -i (squash), bisect, reflog
- resolver conflictos con criterio
- métodos HTTP + idempotencia
- status codes correctos + headers (Cache-Control/ETag/Content-Type)
Salida verificable del módulo: resolver un conflicto de rebase real + hacer squash de una rama antes de un PR; y una API con métodos/status/idempotencia correctos verificados con curl -i.