Программирование — это знания, умения и навыки (можно иногда говорить "искусство", если про талантливых людей) НАХОДИТЬ КОМПРОМИССЫ. Компромиссы между скоростью выполнения задачи (значит стоимостью разработки) и качеством/ясностью вашего кода, между скоростью его исполнения и количеством потребляемой памяти/ресурсов, между наличием/отсутствием тестов и полнотой/качеством их покрытия вашего кода....и множество других компромиссов. . В конце концов программирование — это такой огромный "конструктор", в котором можно складывать фигурки огромное количество раз и вариантов. Какие то варианты будут оптимальные и изящные, но большая часть далеко не такие. Чем лучше навыки-знания, тем лучше вариант. Чем больше попыток (удачных и не удачных), тем больше ваш опыт конструктора.
Любой, пусть даже (вдруг!) изначально "идеально написанный код" по мере развития проекта (хорошо когда бизнес идёт успешно и развивается) может превращаться в говнокод сам по себе, потому что "он обрастает костылями" при добавлении "фич" (нового функционала). Это нормальный и естественный процесс "эволюции кода/системы". Стараться держать в актуальном состоянии юнит/интеграционные/какие_есть тесты, учиться делать рефакторинг как коду, так и тестам— это навыки, которые надо развивать. Можно на личных проектах также, потому что для вас лично они более интересны и ценны по качеству и вы обычно не жёстко лимитированы по времени. Можно "решать задачки", можно участвовать в open source проектах.
"Классические знания/труды" всегда остаются "в силе", про них написали выше, можно для развития ещё читать книги про "искусству создания эволюционирующей архитектуры" ПО. Не для того, чтобы называться сразу "архитектором", а хотя бы просто пробовать заранее планировать разработку, дизайн проекта/модуля/компонента/функции/итд в данном аспекте, оценивать возможные недостатки текущего кода/дизайна/архитектуры. Миддлу это сложно, но в получении новых знаний и навыков нет ничего плохого, если хватает свободного времени (а его всегда крайне мало). Надо понимать, что данная область знаний/профессия продолжает и будет дальше развиваться, новые языки/парадигмы/библиотеки/идеи будут появляться, поэтому самостоятельное развитие (практических) навыков и (теоретических) знаний для практики — залог успеха. Знания без практики и "умесности их применения" в том или другом случае/контексте — тоже могут быть бесполезны и/или вредны. Не надо фанатизма!
Да, иногда сложно "не костылить", да приходится идти с собой на компромисс, да ставить метки todo в коде и заносить в jira задачи "технического долга". Тех долг накапливается и если его не устранять, то рано или поздно проект становится сложно и мучительно поддерживать.
Как разработчик с опытом 18+ в данной профессии — согласен с предыдущими замечаниями. Универсального и единственно работающего рецепта "не говнокодить" нет, приходится развиваться, взрослеть по мере получения практического опыта и знаний.