Why normalize at all
As gradients flow through deep networks, the distribution of activations at each layer drifts (Internal Covariate Shift, in the original 2015 framing). Normalization layers force each layer's input to have a stable mean and variance, which keeps gradients well-conditioned and lets you train deeper, with higher learning rates, more reliably.
BatchNorm
Normalizes across the batch dimension: for each feature, subtract the batch mean and divide by the batch standard deviation. Add learned scale and shift per feature. Used in CNNs (especially ResNet-style). Failure mode: small batch sizes (batch=1, batch=8) give unreliable statistics; behaves differently in train vs eval (running statistics for eval), which is a famous source of bugs.
LayerNorm
Normalizes across the feature dimension within each example: for each example, subtract the per-example mean and divide by the per-example standard deviation. Used in transformers (because batch dimension is awkward for variable-length sequences). No train/eval distinction — same behavior either way.
Where to put it
Two common patterns: Pre-norm (norm before the layer): x = x + sublayer(LayerNorm(x)). Trains more stably for very deep transformers. Post-norm (norm after the layer): x = LayerNorm(x + sublayer(x)). Original transformer convention. Modern transformers default to pre-norm.