YAML, especially how it's used in k8s, is terrible. Writing it by hand is mind-numbingly toilful and extremely error prone, and templating YAML with Go (like Helm does) is a travesty.
The most sensible approach I've seen is to use jsonnet+kubecfg. This lets me use a somewhat sane, composable, turing complete language that spits out a tree of k8s objects. I can use as much or little repeated definitions (or abstractions) as I like. Then kubecfg performs all the kubectl invocations to actually make the cluster state match my calculated definitions.