Стать программистом значит подписаться на обучение длиной в жизнь. Поток нового – новые функции, новые языки, новые инструменты, новые фреймворки – не иссякает никогда. Но вместе с тем, программирование – на удивление верная традициям сфера, где все основывается на принципах, проверенных временем. Мы ввели в оборот объектно-ориентированное программирование, современные аппаратные решения, искусственный интеллект, однако, несмотря на все эти изменения, многие аксиомы, которые были сформулированы еще в прошлом поколении, оказываются верными и на сегодняшний день.
Эту статью я посвятил разбору нескольких из своих любимых высказываний, касающихся программирования. Единственным критерием, по которому я производил отбор, было требование, чтобы цитате сравнялось не менее двадцати лет. Потому что это только устаревшие технологии быстро становятся непригодными к использованию, тогда как древние заповеди наших предков-программистов долго сохраняют актуальность.
1. О косвенности
«Все проблемы в программировании решаются путём создания дополнительного уровня косвенности» — Дэвид Виллер
Вот вам цитата из книги Computer Science Theory and Application, которую все любят повторять и мало кто любит объяснять. Тем не менее, это одна из моих любимых программерских истин – она метко раскрывает саму суть программирования.
Самый простой способ осмыслить косвенность – представить себе слои. Ну например, представим себе, что у вас есть небольшой проект, в рамках которого нужно поместить компонент А внутрь компонента В:
Оба компонента стандартизированы, так что разобрать их на составляющие и поменять принцип работы не получится. Вы могли бы создать отдельный дополнительный компонент (PlugTwoProngVariant
), но это и куча работы, и ненужное дублирование. Есть выход лучше: добавить между этими двумя компонентами слой-адаптер, который успешно взаимодействовал бы с обоими и служил бы между ними посредником.
При все при этом, если бы косвенность исчерпывалась добавлением дополнительных слоев между компонентами, которые иначе не состыкуешь, она была бы, конечно, полезна, но весьма ограничена в применении. Но сама идея того, чтобы решать проблемы, меняя окружение проблемных мест, пронизывает все программирование сверху донизу. Вы сталкиваетесь с ней, когда пытаетесь приладить новую модель данных к старому интерфейсу. Вы сталкиваетесь с ней, когда пытаетесь приладить приложение с legacy-кодом к бэкенду нового веб-сервиса. Вы сталкиваетесь с ней, когда нужно добавить несколько новых высокоуровневых функций вроде логирования и кэширования или скоординировать работу нескольких высокоуровневых сервисов вроде отправки сообщений и проведения транзакций. На самой же вершине этой пирамиды вы приходите к уточенным направлениям вроде машинного обучения (если не можете сами прописать нужное поведение, добавьте еще один слой кода, который решит эту проблему за вас).
Многие скажут вам, что смысл программирования состоит в том, чтобы писать ясные инструкции на языке, который поймет даже самый тупой компьютер. Но цитата Дэвида Виллера предлагает более глубокий взгляд на вопрос. Быть хорошим программистом – значит подниматься по лестнице косвенности, стремясь к самым общим решениям.
Бонусная цитата в тему
Косвенность – мощный инструмент, но за сложность приходится платить. Люди редко приводят то высказывание, которое следует сразу после знаменитой цитаты:
«Обычно это создает новую проблему» — Дэвид Виллер.
Именно благодаря этой истине программисты с давних пор остаются при делах.
2. О простоте
«Простота – предпосылка надежности» — Эдсгер Дейкстра
В мудрых программистах, которые предостерегают нас от усложнения кода без острой необходимости, недостатка нет. Но немногим удалось так явно показать, чем чревата для нас сложность, как пионеру в компьютерных науках Эдсгеру Дейкстра.
Вот в чем тут соль: вы делаете выбор в пользу простоты не просто из желания сделать приятное людям будущего. И не потому что предполагаете возможность повторно использовать этот код в дальнейшем. И не потому что хотите, чтобы он аккуратнее смотрелся на инспекции, и не потому что стремитесь облегчить процесс внесения изменений в будущем (хотя все это, разумеется, ценные преимущества). Вы поступаете так потому, что простота – это предпосылка. Без нее у вас никогда не будет надежного кода, которому можно доверить ведение бизнеса или работу с данными.
Чтобы принять позицию Дейкстра, нам нужно изменить свое понимание того, что такое «хороший код». Это не обязательно самый лаконичный код, или самый быстродействующий, и уж точно не самый заумный. Хороший код – это код, на который можно положиться.
Бонусная цитата в тему
Один из наилучших способов сохранять простоту в коде – помнить, что меньше значит больше. Дейкстра предлагает новую единицу измерения, которая все время будет нам об этом напоминать:
«Если мы хотим подсчитать количество строк кода, следует воспринимать их не как написанные, а как потраченные» — Эдсгер Дейкстра
3. О читабельности и переписывании
«Код сложнее читать, чем писать» — Джоэль Спольски
На первый взгляд, эта цитата Джоэля Спольски, легенды программирования и сооснователя Stack Overflow, кажется разумной, но обманчиво поверхностной. Да, фрагменты кода бывают информационно насыщенными, излишне сжатыми или утомительно длинными. И это относится не только к тому, что писали другие люди. Если вы посмотрите на свои собственные прошлогодние труды, вам понадобится какое-то время, чтобы воссоздать логику, которую вы когда-то знали от и до.
Но наблюдение Спольски разворачивается в нечто интересное. Опасность кода, который с трудом читается, состоит не только в самых очевидных последствиях (его тяжело корректировать и совершенствовать). Есть и другая, большая опасность: сложный для восприятия код кажется хуже, чем есть на самом деле. Фактически, разбираться в чужом коде может показаться такой непосильной задачей, что у вас возникнет искушение совершить то, что Спольски называет грубейшей из всех ошибок – переписать все заново.
Я не говорю, что архитектура системы никогда не выигрывает от подобных переписываний. Разумеется, бывает, что и выигрывает. Но улучшение такого рода дорого обходится. Во всем, что касается тестирования и устранения багов – а это две составляющие разработки, которые отнимают больше времени, чем собственно написание кода – вы возвращаетесь на исходную позицию. Переписывание выглядит заманчиво, потому что укладывается в одно из самых распространенных заблуждений разработчиков – склонность недооценивать трудозатраты на концептуально простые вещи. Именно поэтому 50% времени уходит на заключительные 5% проекта. Элементарные задачи могут отнимать на удивление много времени! А решение проблемы, которую уже решал в прошлом, всегда выглядит проще простого.
Ладно, если переписывать все с нуля, чтобы довести код до совершенства, не следует, то какие есть более удачные альтернативы? Ответ: привлечь каждого разработчика к процессу непрерывного фрагментарного рефакторинга. Так ваш код совершенствуется постоянно, за счет цепочки небольших изменений – реальная выгода с минимальными рисками. Читабельность можно повышать по ходу дела.
Бонусная цитата в тему
Если вы все еще сомневаетесь в важности читабельности, Мартин Фаулер поможет взглянуть на проблему шире:
«Любой дурак может писать код, который будет понятен компьютерам. Хорошие программисты пишут код, который будет понятен людям» — Мартин Фаулер
Иными словами, задача программиста – выдать не просто рабочий код, но код с внутренней логикой.
4. О повторениях
«Не повторяйтесь. Каждый фрагмент знания должен иметь единственное, однозначное, надежное представление в системе» — Энди Хант и Дэвид Томас
Каждый уважающий себя программист знает, что в повторении кроется множество бед. Если вы прописываете одно и то же в нескольких местах, вам приходится больше сил тратить на тестирование и устранение багов. Хуже того, вы создаете условия для возникновения разночтений; например, один фрагмент кода могут впоследствии обновить, а прочие сопутствующие процедуры – не привести в соответствие. Программа с разночтениями – это программа, которой нельзя доверять, а программа, которой нельзя доверять, не может считаться жизнеспособным решением.
Этого бага можно было бы избежать с методом GetTeamUniform()
Однако повторы сеют хаос не только в коде. Данная версия хорошо всем известного принципа DRY (Don’t Repeat Yourself / Не потворяйтесь) толкует принцип устранения дубликатов расширительно, охватывая и другие места, куда могут пробраться повторы. Сейчас разговор идет уже не о дубликатах в коде – мы говорим в том числе и о повторах в масштабах всей системы. А системы кодируют знания в различных форматах. В частности это:
- Операторы
- Комментарии к коду
- Документация для разработчиков или клиентов
- Схемы данных (например, таблицы базы данных)
- Прочие спецификации – планы тестирования, документы по организации процессов, правила сборки
Все эти группы могут пересекаться по своему содержимому. И когда это происходит, возникает риск того, что они начнут транслировать разные версии одной реальности. Скажем, как быть, если в документации описана одна модель работы, а само приложение следует другой? Что в этом случае считать держателем истины? А что если таблицы в базе данных не соответствуют модели данных из кода? Или если комментарии к коду описывают операцию или алгоритм, которые в корне отличаются от реальной имплементации? Каждая система нуждается в единственном надежном представлении, на которое опирается всё остальное.
Кстати говоря, не следует думать, что конфликты между претендентами на истину случаются только в небольших проектах или являются следствием низкого качества кода. Один из самых лучших примеров, который полыхнул у всех на виду – битва между XHTML и HTML5. Одна сторона утверждала, что спецификации – это и есть официальная правильная версия, а браузеры должны под нее подстроиться. Другой лагерь возражал, что именно поведение браузеров должно считаться стандартом де-факто – ведь именно так проектировщики всё себе и представляли, когда писали веб-страницы. В конечном итоге, победила та версия истины, которую продвигали браузеры. С тех пор HTML5 – это то, что браузеры реально делают, включая допустимые короткие пути и ошибки.
Бонусная цитата в тему
Возможность того, что код и комментарии к нему вступят в противоречие друг с другом, породила оживленные дискуссии: чего вообще больше от комментариев – пользы или вреда? Сторонники экстремального программирования относятся к ним с откровенным недоверием.
«Код никогда не лжет, а вот с комментариями такое случается» — Рон Джеффрис
5. О сложных проблемах
«В компьютерных науках есть только две сложные проблемы – аннулирование кэша и придумывание названий» — Фил Карлтон
С виду эта цитата кажется просто программисткой шуткой, забавной, но ничем не выделяющейся из прочих. Каждый может прочувствовать контраст между чем-то, что звучит как сложная задача (аннулирование кэша), и чем-то, что звучит как сущий пустяк (придумывание названий). Любой программист хоть раз убивал целые часы на какую-нибудь до смешного мелкую проблему – два параметра, проставленных не в том порядке, переменную, которая где-то с большой буквы, а где-то нет (спасибо, JavaScript!). Пока людям для достижения своих целей приходится работать совместно с компьютерами, программирование всегда будет представлять собой смесь высокоуровневого системного планирования и дурацких опечаток.
Но если вчитаться в слова Фила Картона повнимательнее, мы обнаружим здесь больше простора для размышлений. Придумывать названия сложно не просто потому, что из таких маленьких головных болей у программиста то и дело вся жизнь идет кувырком. Дело тут еще и в том, что названия – это одна из граней основной задачи программиста, проектирования программ. Иными словами, как вообще пишется ясный, аккуратный и непротиворечивый код?
Существует много разновидностей плохих названий. Все мы встречались с переменными, которые нарекли в честь типов данных (myString
, obj
), сокращений (pc
, то есть product catalog), какой-нибудь незначительной детали имплементации (swappable_name
, formUserInput
) или же вообще оставили безымянными (ret_value
, tempArray
). Легко попасться в ловушку и назвать переменную исходя из того, что вы сейчас с ней делаете, а не из ее содержимого. А со значениями логических типов данных вообще беда: что подразумевает progress
– что прогресс уже начался, что нужно отобразить информацию о прогрессе в интерфейсе или вообще что-то третье?
Источник: CommitStrip.com
Но названия переменных – это еще только начало. Когда вы начинаете придумывать имена классам, встает вопрос о том, как разбивать код на независимые части. Названия публичных членов определяют, каково будет представление интерфейса, с помощью которого разные части приложения будут взаимодействовать между собой. Закрепляя за фрагментом кода название, вы не просто описываете, что он может делать – вы устанавливаете, что он будет делать.
Бонусная цитата в тему
«В компьютерных науках есть только три сложные проблемы – аннулирование кэша, придумывание названий и ошибка на единицу» — Леон Бамбрик
Автор: DigitalEcosystems