Most important lesson for me is to stay close to the problem domain. Think deeply about what your domain means and model in code accordingly.
Staying close to the true meaning of your application beats all other attempts at code maintainability. Types, tests, frameworks, dry, language features, ci pipelines, scrum whatnot. All just fun party tricks that fall short when your building the wrong thing.
Are we building the wrong thing? Almost always.