Есть два типа языков: языки, где есть только один очевидный/правильный путь реализовать что-либо (Python, Java), и языки, где одну и ту же фичу можно закодить тысячей и одним способом (C++, JavaScript, Ruby). Первые больше похожи на детские конструкторы, вторые - на пластелин.

Подробнее LEGOs, Play-Doh, and Programming и инфографика.

И если для первой группы языков имеет смысл перерывать StackOverflow в поисках единственно-истинного решения отсортировать массив, то для второй группы обычно существует сразу множество "верных" решений. Зачастую, эти решения даже не являются компромиссными. Т.е. с точки зрения скорости выполнения, используемой памяти, поддержки кода и других аспектов абсолютно все равно, какое из решений выбрать.

Собственно проблема заключается в необходимости измененить способ мышления после длительного использования конструкторо-подобных языков для эффективного использования пластилиновых. Перфекционист внутри разработчика заставляет его каждый раз стрелять золотыми пулями. В итоге часы/дни/недели уходят на поиск "верной" реализации наследования, способа обработки ошибок, организации классов/модулей и пр., хотя на самом деле язык позволяет "приготовить" эти вещи несколькими равноправными способами. И в то время, как для конструкторо-подобных языков неиспользование общепринятых подходов в конечном итоге приводит к плохому коду, для пластилиновых языков попытка загнать себя в несуществующие рамки также негативно сказывается на результате. Разработка замедляется или вовсе останавливается, зацикливаясь на переписывании одних и тех же участков кода.

Решение, на мой взгляд, состоит в том, что для языков второго типа необходимо "освободить" свой мозг и просто позволить ему творить. Принять для себя, что главное - это написать рабочее решение. Конечно, в процессе разработки будут попадаться не совсем "те" решения, но со временем они будут "выправлены" возникающими неудобствами/противоречиями. И хотя это означает, что часть кода все же придется переписывать - эти переписывания будут обусловлены реальными требованиями (в отличие от циклических переписываний в процессе поиска идеального варианта). Существует и эффективный способ как можно скорее обнаружить неудачное решение - написание тестов. Если код трудно оттестировать - это верный признак неудачного архитектурного решения.