10 buenas prácticas en Angular que transformarán la calidad de tu código
Angular ha cambiado mucho en los últimos años (standalone, señales, nuevo control flow). Aun así, la mayoría de los problemas que vemos en auditorías no vienen de “falta de features”, sino de decisiones pequeñas y repetidas que degradan la calidad: tipado laxo, detección de cambios descontrolada, RxJS complejo, rutas sin lazy, falta de tests útil…
En esta guía directa y accionable reunimos 10 prácticas que aplican tanto si empiezas un proyecto nuevo como si mantienes uno “veterano”. Verás qué hacer, por qué y cómo con ejemplos cortos. Al final, te dejamos una checklist imprimible para que el equipo adopte estas mejoras sin fricción.
Para quién es
Tech leads, devs y responsables de producto que buscan código más mantenible, rendimiento predecible y menos sorpresas en producción.
Índice rápido
- Inicia con strict mode y linters
- Usa componentes standalone y organiza por features
- Activa
OnPusho señales para detectar cambios - Tipado estricto en TypeScript y en plantillas
- RxJS sin dolor: composición y cancelación
- Manejo de estado pragmático (señales/tiendas ligeras)
- Ruteo con lazy loading y separación por dominios
- Rendimiento en plantillas (
trackBy,@for, imágenes) - Tests útiles (unit, harnesses, integración ligera)
- Seguridad, accesibilidad y observabilidad desde el día 1
1) Inicia con strict mode y linters
La consistencia evita deuda técnica.
Checklist mínimo:
-
ng new app --strict(o activastrictentsconfig.json).- ESLint + reglas de Angular; Prettier solo para formato.
- Scripts NPM:
lint,typecheck,test,build. - Hooks
pre-commitpara prevenir code smell.
{
"scripts": {
"lint": "ng lint",
"typecheck": "tsc --noEmit",
"test": "ng test --watch=false"
}
}
Resultado: menos sorpresas en producción y PRs más rápidos.
2) Usa componentes standalone y organiza por features
Los standalone reducen boilerplate y simplifican dependencias. Organiza la app por dominios (features), no por tipo de archivo.
src/app/
core/ # cross-cutting (interceptors, guards)
shared/ # UI reutilizable
features/
orders/
pages/ components/ data-access/ utils/
@Component({ selector:'app-order-list', standalone:true, template:`...` })
export class OrderListComponent {}
Resultado: escalabilidad y testeo más simple.
3) Activa OnPush o señales para detectar cambios
Reduce renders innecesarios con ChangeDetectionStrategy.OnPush o adopta señales (signals) para un modelo reactivo preciso.
@Component({ changeDetection: ChangeDetectionStrategy.OnPush })
export class CardComponent { /* inputs inmutables */ }
const subtotal = signal(100);
const iva = signal(0.21);
const total = computed(() => subtotal() * (1 + iva()));
Resultado: menos bugs, autocompletado mejor y refactors seguros.
4) Tipado estricto en TypeScript y en plantillas
Deja que el compilador te proteja.
- Evita
any: usa tipos discriminados y utilidades (Pick,Omit). - Marca opcionales/estados de carga explícitamente.
- En plantillas, usa
input.required<T>()y salidas tipadas.
export type Order = Readonly<{ id:string; total:number; status:'pending'|'paid' }>
Resultado: menos bugs, autocompletado mejor y refactors seguros.
5) RxJS sin dolor: composición y cancelación
No todo tiene que ser un Observable, pero cuando lo uses, hazlo bien.
- Prefiere
switchMap,combineLatest,withLatestFrom. - Usa
takeUntilDestroyed()para auto-unsubscribe. - En plantillas,
asyncpipe o señales.
this.total$ = timer(0,60000).pipe(
switchMap(() => this.http.get<{total:number}>('/api/metrics')),
map(r => r.total)
);
Resultado: flujos legibles y sin fugas de memoria.
6) Manejo de estado pragmático (señales/tiendas ligeras)
Elige la herramienta por complejidad, no por moda.
- Local/simple: señales +
computed. - Compartido: signal store o servicio con señales.
- Crítico/enterprise: NgRx cuando necesites auditoría, undo/redo, normalización.
@Injectable({providedIn:'root'})
export class OrdersStore {
private _orders = signal<Order[]>([]);
readonly total = computed(() => this._orders().reduce((a,o)=>a+o.total,0));
set(v:Order[]){ this._orders.set(v); }
}
Resultado: menos fricción y trazabilidad suficiente.
7) Ruteo con lazy loading y separación por dominios
Carga lo necesario, cuando hace falta.
- Rutas por feature y lazy por defecto.
- Evita mega-módulos; aprovecha standalone en rutas.
export const routes: Routes = [
{ path: 'orders', loadComponent: () => import('./features/orders/pages/orders.page').then(m => m.OrdersPage) }
];
Resultado: Time-to-Interactive menor y mejores Core Web Vitals.
8) Rendimiento en plantillas (trackBy, @for, imágenes)
Evita renders completos de listas y cuida los recursos.
- Usa
@for (item of items; track item.id)o*ngFor trackBy. - Carga diferida de imágenes y recursos (
loading="lazy"). - Considera
NgOptimizedImageparasrcsety tamaños.
Resultado: listas grandes y catálogos que vuelan.
9) Tests útiles (unit, harnesses, integración ligera)
No persigas el 100% de cobertura; persigue confianza.
- Unit: lógica pura y pipes.
- Component: Angular Testing Library o Harnesses.
- Integración ligera: rutas/guards/interceptors clave.
- E2E donde aporte negocio (feliz + crítico).
Resultado: refactors rápidos con red de seguridad real.
10) Seguridad, accesibilidad y observabilidad desde el día 1
Calidad = también proteger, incluir y medir.
- Seguridad:
HttpInterceptorpara headers, sanitización de HTML, Content Security Policy. - Accesibilidad (a11y): roles ARIA, foco gestionado, contraste, navegación por teclado.
- Observabilidad: logs estructurados, métricas UX (CLS, LCP), error boundary con
ErrorHandler.
Resultado: menos incidentes y experiencia consistente.
¿Quieres llevar esto a tu proyecto?
En TechsBCN ayudamos a equipos a modernizar Angular: auditoría de calidad, guía de migración a standalone & signals, performance clinic y playbooks de pruebas. Hablemos para revisar tu código y priorizar impactos en 2 semanas.
