
Технический вкус — это не то же самое, что технический навык. Вы можете быть технически подкованы, но иметь плохой технический вкус и наоборот. Как и любой вкус в принципе, технический вкус зачастую не зависит от наличия фактического навыка. Вы же можете отличить хорошую еду от плохой, не умея готовить? То же самое с ПО — вы вполне способны понять, нравится ли оно вам, не имея возможности его создать. И если технические навыки можно наработать через усердное изучение и повторение, то хороший вкус вырабатывается менее очевидным путём.
Вот некоторые индикаторы его наличия:
Какой код вы сочтёте «хорошим»? А какой «плохим»?
Какие проектные решения вам действительно импонируют, а какие просто устраивают?
Какие программные задачи интересны для вас настолько, что не выходят из головы даже после работы? А каких задач вы стараетесь избегать?
Я считаю, что вкус — это умение адаптироваться под набор инженерных ценностей, которые подходят для вашего текущего проекта.
Почему вкус и навык — это разные вещи
Но разве все перечисленные индикаторы не являются просто частью навыка? К примеру, не будет ли код выглядеть хорошим, если он действительно хорош? Не думаю.
Разберём практический случай. Лично я считаю, что код с использованием функций map и filter выглядит лучше, чем построенный на цикле for. И с позиции разработки такое мнение вроде бы логично. Например, map и filter обычно задействуют в работе более понятные чистые функции и исключают целый класс ошибок итерации, а именно ошибок на единицу (off-by-one error). Мне кажется, что это уже не вопрос вкуса — в этом случае я прав, а те, кто считает иначе, ошибаются.
Но это резкое заявление, и я понимаю, что на деле всё намного сложнее. В языках вроде Go принципиально нет функций map и filter. С позиции производительности перебор элементов при помощи for вроде как проще, и его легче расширить на другие подходы к итерации (например, когда перебирается по два элемента за раз). Но все эти причины важны для меня не так сильно, как преимущества от использования map и filter. Именно поэтому я не пишу много циклов for — но с моей стороны было бы слишком надменным утверждать, что разработчики, которые предпочитают использовать эти циклы, менее опытны. Во многих случаях у них есть технические навыки, которых нет у меня. Просто для них важнее другие аспекты.
Иными словами, наше расхождение во мнениях сводится к различиям в ценностях. Я писал об этом в статье «I don’t know how to build software and you don’t either». Даже если для этих широких технических дебатов и существуют абсолютные ответы, ни один разработчик не может их знать, так как весь опыт индустрии в карьеру одного отдельного человека не втиснуть. Мы все как минимум отчасти опираемся на собственный опыт — на свой конкретный набор инженерных ценностей.
Что же такое вкус в разработке
При разработке ПО почти все решения представляют собой компромисс. Редко бывает, что мы выбираем из двух опций, среди которых одна явно лучше. Чаще каждая из этих опций имеет свои плюсы и минусы. Нередко приходится делать трудный выбор между ценностями — после достижения определённой точки уже не получается легко наращивать производительность кода без ухудшения, к примеру, его читаемости.1
Реальное понимание этого, на мой взгляд, является сильнейшим индикатором зрелости в сфере разработки ПО. Незрелые разработчики часто категоричны в принятии решений. Они считают, что всегда правильнее использовать подход Х, а не Y. Опытные же специалисты обычно с готовностью рассматривают все варианты, так как понимают, что каждый из них имеет свои преимущества. Смысл не в том, чтобы определить, какая технология лучше — X или Y — а в том, перевесят ли преимущества X возможности Y в конкретном случае.
Иными словами, незрелые разработчики излишне догматичны в своём вкусе. Они осознают, что им нравится, но принимают это видение за принципиальную позицию разработки. Так что же определяет конкретный вкус разработчика?
На мой взгляд, ваш вкус включает в себя целый набор ценностей, которые вы находите наиболее важными. Например:
Отказоустойчивость. Если компонент инфраструктуры даёт сбой (падает сервис, пропадает сетевое подключение), продолжит ли система работать? Может ли она восстанавливаться без вмешательства человека? Не затесались ли в критический путь программы какие-то не самые важные операции?
Читаемость. Легко ли воспринимается ПО на первый взгляд, и насколько быстро с ним можно познакомить новых разработчиков? Не растянуты ли функции, и насколько грамотно они проименованы? Хорошо ли система задокументирована?
Корректность. Есть ли возможность выразить в системе недопустимое состояние? Насколько система ограничена тестами, типами и утверждениями? Используются ли в тестах техники вроде фаззинга? И для самых крайних случаев, доказывалась ли корректность программы формальными методами вроде Alloy?
Гибкость. Легко ли расширять систему? Насколько сложно вносить в неё изменения? Если мне нужно что-то изменить, сколько частей программы придётся затронуть?
Портируемость. Привязана ли система к некой конкретной операционной среде (например, Microsoft Windows или AWS)? Если систему нужно будет развернуть где-то ещё, много ли для этого потребуется инженерных усилий?
Масштабируемость. Если трафик увеличится в 10 раз, система выдержит? А если в 100 раз? Потребуется ли ей выделение дополнительных ресурсов, или же она сможет масштабироваться автоматически? Какие узкие места потребуют вмешательства разработчиков?
Скорость разработки. Если мне нужно расширить систему, как быстро я смогу это сделать? Сможет ли с ней работать большинство инженеров, или же потребуется привлечение эксперта в предметной области?
В контексте проектирования систем есть и много других ценностей: изящность, современность, использование опенсорсных решений, денежная стоимость поддержания работы и так далее. Всё это важно, но ни один разработчик не будет придавать равную значимость каждому пункту. Ваш вкус определяется тем, какие из ценностей вы ставите во главу угла. Например, если для вас важно получить производительную и корректную систему, а не ускорить её разработку, то вы предпочтёте использовать Rust, а не Python. Если вам важнее обеспечить масштабируемость, а не портируемость, то вы наверняка сделаете упор на конкретных инструментах и особенностях вашего провайдера (например, AWS). Если вы готовы пожертвовать скоростью ради отказоустойчивости, то, скорее всего, разделите трафик между разными регионами. И так далее.2
Но все эти ценности можно разбить на ещё более детальные аспекты. Например, два разработчика, для которых очень важна читаемость, могут разойтись в своих мнениях, так как один предпочитает короткие функции, а другой — короткие стеки вызовов. Или же, если два разработчика в равной степени ценят корректность, то они могут поспорить из-за того, что один будет склонен к выполнению исчерпывающих тестов, а другой предпочтёт формальные методы проверки. Но суть здесь одна — в сфере проектирования ПО есть много разных ценностей. И поскольку они часто находятся в противофазе, каждому разработчику приходится делать акцент на одних в ущерб другим.
Как определить плохой вкус
Я уже говорил, что все перечисленные ценности важны, но несмотря на это, у человека может быть плохой вкус. В контексте разработки ПО это означает, что ваши ценности не очень подходят для проекта, над которым вы работаете.
Большинству из нас доводилось работать с такими людьми. Они приходят в проект, яро отстаивая какие-то свои идеи, предлагая использовать формальные методы, переписать всё на Go, применить мета-программирование Ruby, развернуть приложение в разных регионах и прочее — просто потому, что на их опыте это когда-то хорошо сработало. Независимо от того, является это правильным решением для вашего проекта или нет, они будут его отстаивать, так как это их вкус. И не успеешь глазом моргнуть, как уже будешь стремиться обеспечить максимальные показатели надёжности системы, делая её при этом совершенно непонятной для джунов.
Иначе говоря, основная причина плохого вкуса — это догматичность. Я никогда не буду доверять разработчику, который оправдывает свои решения словами «Это наилучший подход». Ни одно инженерное решение не является «наилучшим» во всех контекстах. Правильность того или иного подхода всегда будет зависеть от конкретной задачи.
Думаю, что разработчики с плохим вкусом подобны сломанным компасам — если вы находитесь в правильном положении, такой компас исправно будет указывать на Север. Но стоит вам начать перемещаться, он тут же собьёт вас с пути. То же касается разработчиков с плохим вкусом. Они могут вполне эффективно работать в конкретной области, где их предпочтения соответствуют потребностям проекта. Но в случае перехода на другой проект или работу процесс сразу выходит из колеи. Ни одна должность не остаётся неизменной долгое время, особенно в эти непростые времена после 2021 года.
Как определить хороший вкус
Разглядеть хороший вкус гораздо труднее, нежели технический навык. Дело в том, что в отличие от навыка, вкус представляет умение выбирать правильный набор ценностей для конкретной технической задачи. Поэтому определить наличие хорошего вкуса намного сложнее: его нельзя проверить на смоделированных задачах или простыми вопросами о технических фактах. Задача должна быть актуальной и включать запутанный контекст реальной действительности.
Утверждать о наличии хорошего вкуса можно тогда, когда проекты, над которыми вы работаете, преуспевают. Если вы не вносите ощутимый вклад в структуру проекта (например, просто обрабатываете тикеты), то признаком наличия у вас хорошего вкуса будет успешность тех направлений, со структурой которых вы согласны, и провал тех, структура которых вам не по душе. И здесь важна причастность к проектам разного вида. Если речь идёт лишь об одном проекте или о повторении аналогичных, то вы можете просто оказаться к ним удачно подготовленным. И даже если вы пройдёте через серию разных проектов, нет гарантии, что у вас есть хороший вкус в мало знакомых вам областях.3
А как выработать хороший вкус? Сложно сказать, но я советую работать в разных направлениях, обращая внимание на то, какие проекты или их части даются легко, а какие тяжело. Фокусируйтесь на гибкости: старайтесь не формировать категоричных универсальных мнений о том, как правильно писать ПО. Свой вкус я вырабатывал довольно долго, хотя не вижу препятствий для его ускоренного формирования у других. Уверен, есть одарённые люди, вкус которых значительно превосходит их опыт программирования. Здесь всё так же, как и в любых других областях.
Дополнение: эта статья вызвала бурное обсуждение на Hacker News, среди которого возникла интересная дискуссия на тему того, как вырабатывать хороший вкус у новичков. Было высказано мнение, что нужно как бы слегка зеркалить их вкус, чтобы они лучше видели и понимали его особенности. Некоторые комментаторы утверждали, что «вкус» не играет роли в разработке ПО. Они считают, что все задачи имеют единственно верное решение, к которому разработчики должны прийти аналитическим путём. Мне такое видение кажется вздором. Ведь очевидно же, что для любой инженерной задачи есть множество приемлемых решений, и в определённые моменты выбор сводится к личному предпочтению. Другие участники отметили, что я не упомянул о клиентах и бизнесе — согласен, но мне больше хотелось написать о том, что даже чисто технические решения подвержены влиянию вкусовщины.
Примечания
Естественно, это не всегда так. Бывают ситуации «win-win», когда можно одновременно улучшить ряд обычно противоречащих друг другу ценностных аспектов. Но всё же такие ситуации возникают редко. ↩
Как я уже говорил, в разных проектах, естественно, будут актуальны разные наборы ценностей. Но специалистам, работающим над этими проектами, всё равно придётся где-то провести черту, и здесь уже они будут опираться на личный вкус. ↩
Тем не менее я считаю, что хороший вкус человека может расширяться на другие сферы. У меня нет особого опыта по этой части, поэтому я решил написать примечание. Но если у вас достаточно гибкое мышление, и вы внимательны к деталям в области А, то наверняка также окажетесь гибким и внимательным к деталям в области В. ↩
Комментарии (10)

OlegZH
05.10.2025 11:29Какой код вы сочтёте «хорошим»? А какой «плохим»?
Довольно наивно высказывать здесь своё мнение, будучи, по сути, начинающим программистом. Но я, всё же, скажу пару слов.
Мне, именно как новичку, крайне важно, чтобы код был понятным. Чтобы надо было затратить минимальное время на его освоение. В таком коде все используемые концепции будут выражаться явным образом, и нам не придётся только по прочтении целого фрагмента понимать, что он делает. Код должен быть развёрнутым (хорошо разобран по различным функциям) и документированным. В этом смысле, пример с функциями типа
map/filter/reduce— это, как раз, тот самый пример, когда код документирует сам себя, когда в коде в явной форме отображается использованная в нём идиома или вычислительная концепция.Разберём практический случай. Лично я считаю, что код с использованием функций
mapиfilterвыглядит лучше, чем построенный на циклеfor. И с позиции разработки такое мнение вроде бы логично. Например,mapиfilterобычно задействуют в работе более понятные чистые функции и исключают целый класс ошибок итерации, а именно ошибок на единицу (off-by-one error). Мне кажется, что это уже не вопрос вкуса — в этом случае я прав, а те, кто считает иначе, ошибаются.Но это резкое заявление, и я понимаю, что на деле всё намного сложнее
Чем хорошо использование
map/filter/reduce? Тем, что Вы в явной форме описываете то, что хотите сделать. Тем, что Вы подчёркиваете векторную структуру исходных данных, и возможность единообразного применения к элементам этой структуры одной и той же функции. Применениеmap/filter/reduceпозволяет автоматизировать написание кода (когда Вы каждый раз выбираете функцию обработки, функцию предобработки и функцию постобработки) и приводит к обобщениям (когда конструкциюmap/filter/reduce) можно пометить в аналогичную конструкциюmap/filter/reduceболее высокого порядка.Иногда, конечно, хочется какого-то большего разнообразия движений. Например, можно попробовать написать код, вроде этого:
def func_apply(main_func, input_array, filter_func=None, reduce_func=None): def process_element(array, i): input_value = array[i] value = None if not filter_func or (filter_func and filter_func(input_value, array)): value = main_func(input_value) if value is None: return None output_value = None if not reduce_func or (reduce_func and reduce_func(value, array)): output_value = value return output_value n = len(input_array) output_array = [ process_element(input_array, i) for i in range(n) ] return output_arrayЗдесь мы пытаемся использовать предусловие и постусловие и формируем на выходе массив, аналогичный по структуре исходному.

ALexKud
05.10.2025 11:29Напрашивается стандартное "на вкус и цвет товарища нет". Все зависит от задач, используемого языка, личных предпочтений. Так как я использую в основном серверные базы данных, то всю обработку вешаю на сервер со всеми map,filter и reducе в SQL хранимых процедурах. У меня задачи позволяют использовать такую архитектуру двухзвенки. На клиенте только интерфейс. Получается быстро, можно оптимизировать SQL и т д и т п. Интерфейс сейчас в двух вариантах - DELPHI и LABView и бизнес логика приложений в БД довольно непростая. Работает все быстро, даже при подключении удаленных подразделений через VPN. Есть конечно и PHP приложения, но это в общем GRUD приложения, весьма капризные по отношению к железу. На старых компах есть с ними проблемы. К сожалению, современные программисты в большинстве своем плохо знают SQL и его возможности и во многих задачах всю обработку вешеют на код клиента, да еще в трехзвенке. Это вот на мой взгляд вкус так себе.
Emelian
Я перелопатил «туеву хучу» опенсорсного кода на С++ и не разу он мне не понравился с точки зрения оформления и структуры.
Такое впечатление, что абсолютно все «приплюснутые» программисты, включая и наших и зарубежных, учились по одним и тем же сомнительным источникам, в части советов по стилю программирования.
В Си не нравится «монолитность» кода, точнее код там, обычно, оформлен как поток сознания их авторов, ничем не ограниченным «снаружи».
С++ – потенциально явно лучше. Он позволяет хорошо структурировать код на классы, неймспэйсы, файлы и каталоги, проекты и решения. Это, для меня, просто супер!
Но, вот стиль программирования, как правило, у всех ужасен. Главные претензии:
Когда нужно ставить разделители (пробелы и новые строки), – их не ставят, если это позволяет делать компилятор.
Когда не нужно делать лишние разделители – их, наоборот, делают.
Комментирование кода, обычно, небрежное.
Заголовки, сверху и снизу (в виде комментариев) для многострочных конструкций языка – не делаются либо делаются по принципу: «Нате вам и отстаньте!».
Не разделенная, мною, любовь всех злоупотреблять нижним регистром символов и излишне частым использованием символом «нижнее подчеркивание».
Часто, в одном файле, – слишком много классов.
Также не нравится, в пользовательских проектах, делать реализацию функций в h-файлах. Для библиотечных исходников, вроде, WTL – это нормально, поскольку их код я редактировать не намерен.
Ну, и так далее, по мелочам. Поэтому, Си-шный опенсорс я переделываю в С++-классы, вроде, консольного видеопроигрывателя FFPlay.c (скриншот подобной реализации для моей оконной программы можно посмотреть в http://scholium.webservis.ru/Pics/MediaText.png ).
А весь доступный «приплюснутый» код, я подвергаю, перед использованием, стилевому рефакторингу, под свой вкус, иначе, я не могу с ним работать. Хорошо, что, как программист, я всегда работал один. В команде я бы не ужился, разве что, только, в должности начальника «1С», по принципу: «Я начальник – ты дурак!» :) .
Для чужих проектов – те, которые компилируются без проблем. Если взять произвольный проект на С++, не говоря уже о Си, на Гитхабе и попробовать его скомпилировать из «коробки», то, с вероятностью 90% – не получится. Нужно, предварительно, долго плясать с бубном, чтобы данный проект просто скомпилировался. Не нравится, что авторы редко прикладывают к своим «шедеврам» скриншоты, не говоря уже о готовых бинарниках.
Поэтому, если чужой проект легко скомпилировался, то, тогда, даже его ужасный стиль не так раздражает. Ибо к стилевому рефакторингу мне не привыкать, тем более, что, при этом, лучше вникаешь в, собственно, код.
Интересны проблемы пользовательского интерфейса на C++ / WTL. Вот, неожиданно, для себя, столкнулся с вопросами рисования кнопок. Для кастомизации кнопок существует несколько методов. Наиболее типичные, это полностью собственные классы, наследование классов стандартных контролов, с внутренним сабклассингом либо без, и внешним сабклассингом стандартных элементов управления.
Мне нужна эмуляция кнопок, исключительно средствами пользовательского рисования. Другие методы работают, и даже этот метод работает, но «безоконное» рисование дочернего компонента (точнее, непосредственное рисование в главном окне приложения) приводит к «перемигиванию» других контролов, хотя, двойную буферизацию и обновление только субобластей, я использую часто, но, в данном случае, в буферизации не вижу смысла, а частичное обновление, почему-то, не работает.
Эти кнопки должны завершить мой очередной пет-проект, который может работать и без них (за счет клавиатурных команд, вроде, «энтера»). Однако хочется добавить их для «красоты», так, чтобы они не мешали работе других контролов. А почему мешают? – Пытаюсь разобраться.
Однако, поскольку я программирую как «свободный художник», то неинтересными задачами я просто не занимаюсь. Зачем? Когда и интересных хватает.
Bright_Translate Автор
Шикарный предметный комментарий
OlegZH
Совсем ни разу?
Но это же не значит, что на C/C++ нельзя писать так, чтобы было красиво. Наверное.
А я думал, что в C наблюдается разнобой в типе возвращаемых значений, встречаются макросы, а ещё надо тщательно следить за звёздочками, а то можно сильно промахнуться с памятью или с типом переменной. А ещё в прежние времена было много мороки в том, какая модель памяти, длинные и короткие указатели, всякие магические константы.
А можно ли представить C-образный язык, который будет уже совсем хорош в плане внешнего вида и семантики? Go? Rust?
Сегодня языки как-то в общем сближаются между собою. Специализация утрачивается. Многое упирается в традицию, в наличие библиотек/сред разработки. Синтаксис важен. но второстепенен.
Emelian
С разбегу не назову идеальный, на мой взгляд, проект, в части стиля. Да, я уже и отвык от мысли, найти подобный. Достаточно, что код делает то, что мне нужно, а сделать его «стилевой рефакторинг», для меня, не проблема, тем более, что это полезно, так как помогает лучше «почувствовать» код.
Да, для себя я стараюсь писать красиво. По стилю, это меня устраивает, но, по структуре, часто сомневаюсь, а, правильно ли я тут все структурирую?
Код консольного видеопроигрывателя FFPlay.c, который я переделывал в классы С++, достаточно хорош, как по содержанию, так и по си-шному стандарту оформления, наверное, тоже. Только мне нужны были классы, оформленные в привычном, для меня, стиле. Только и всего.
Меня С++ // WTL устраивают более, чем. Другие Си-подобные языки я вообще не вижу резона изучать.
Может быть. Я ведь говорю только о себе, даже если высказываю свое мнение о других. А кто я такой, чтобы судить о других? Мне очень нравится легкий WTL, а его масса более тяжеловесных аналогов – нет. Но, раз их создают, значит, их кому-то нужны? А лично для меня: «В этом Мире есть много вещей, которые мне… не нужны!».
OlegZH
Звучит как приговор. Интересно, а можно взять какой-то открытый компилятор (gcc?) и выкладывать код для данного компилятора? Правда, сегодня, компилятор можно встроить прямо в сайт.
Кстати, а ИИ позволяет исправлять плохой стиль программного кода?
Emelian
Любые утверждения надо воспринимать критически. И мои тоже. Я, ведь, забыл сказать, что использую для компиляции только компилятор VS C++ «средних версий». И, даже там, если версия «высокая», то сразу возникает несовместимость. Иногда ее удается легко преодолеть, иногда, нет.
Соответственно, другие компиляторы, вроде, gcc, тоже не использую. Поэтому, если все компилировать в их родных компиляторах, то статистика может стать с точностью до наоборот. Т.е., 10%, с разбегу не компилируются, а остальные – вполне. Но, если бы все авторы прикладывали бы к своим творениям хотя бы скриншоты, то проблемы бы здесь для меня вообще бы не было. Вот, когда понравились скриншоты китайской библиотеки интерфейса «DuiLib» и ее собственного редактора «DuiLib Editor», для внешней правки xml-файлов пользовательского интерфейса, то скомпилировал их, хотя, пришлось, конечно, повозиться. Потом, я, правда, от нее все равно отказался в пользу WTL, но полученный опыт остался.
Я пользуюсь только бесплатными версиями ИИ-сервисов. А как они могут выдавать красивый, в моем понимании, код, если его не существует в природе? Стиль такой же, как и на Гитхабе, один в один.
OlegZH
Простите, а можно где-нибудь прочитать сишный код, который "оформлен как поток сознания"?
(Стало очень интересно на это посмотреть, поковырять и попробовать что-то с таким кодом сделать. Например, на том же C. Или уже на C++. А можно и свой язык придумать. Так, чтобы его можно было бы транслировать в C/C++. Или изначально писать на C/C++, но уже транслировать во что-то другое.)
Emelian
Ну, я уже упоминал файл «ffplay.c» из упаковки «ffmpeg» (ищите на Гитхабе) в каталоге «fftools». Кроме этого, там много других аналогичных c- и h-файлов, которые нужно использовать для компиляции консольного видеопроигрывателя. В бинарной упаковке FFMpeg есть уже скомпилированный файл «ffplay.exe». Можете посмотреть, как он работает.
Можете также посмотреть файл «sqlite3.c», размером, более восьми мегабайт, на Гитхабе. Что это, как не «поток сознания»? Хотя, кто-то может возразить. Мол, файл «sqlite3.c» это просто сборка массы отдельных с-файлов, которые делали десятки энтузиастов. Согласен, но мне от этого не легче. Менять что-то там резона особого нет, ибо чревато, хотя, для адаптации консольного видеопроигрывателя, в оконной программе, менять пришлось много чего. Но, по сравнению с SQLite, он, все же, проще.