Страницы

Мысли о программировании

Короткие разрозненные утверждения

В программировании всё не совсем то, чем кажется. Фильмы, подобные «Матрице» и «Началу» — это, во многом, художественное оформление того, что в нём происходит — сны внутри снов внутри ещё одних снов. Мы придумали метафоры и слишком серьёзно поверили в них. Часто видим непроходимость препятствий там, где её нет, и не видим возможностей, когда они есть, но «так не принято».

Очень просто сделать язык, в котором разрешено всё. Но главное в языке всё же то, чего делать в нём нельзя. Большая часть потенциально возможных вычислений бесполезна, а полезные составляют лишь ничтожнейшую часть от них. Одна из задач хорошего универсального языка — уменьшение мощности множества бесполезных программ, возможных на нём, без заметного уменьшения множества полезных.

В простом и скромном языке существует правило: «Не включайте никогда то, что надо иногда».

Потребность в комментировании не учебного кода, включая цели документации, свидетельствует о недостаточной выразительности исходного языка программирования, не позволяющей напрямую выразить мысль в коде. Преподнесение документации кода как «хорошей» практики говорит о проблемах в индустрии. Она или занимается формалистическим культом, или не способна создать либо внедрить формальный язык, который был бы понятен хотя бы не хуже естественного. А ведь ещё в 60-х язык программирования Algol создавали в первую очередь для людей, а потом уже для машин. Какая деградация замысла!

Программирование как широкое поле деятельности не сложно само по себе. Сложно лишь сложное программирование, составляющее только часть всего полезного. Ещё часть сложностей возникает от переусложнений.

Вся идея о разделении машины на аппаратное и программное обеспечение затевалась ради программного и только ради него. Если бы можно было бы запускать программы вообще без аппаратуры, так бы и следовало сделать. В аппаратуре остаётся то, что невозможно перенести в программную область либо вообще, либо без потери нужных ресурсных характеристик. Несомненно, было бы прекрасным запускать ПО в виде самодостаточных энергетических потоков — этот подход обладал бы великолепной масштабируемостью, просто, так не получается.

Программы появились не потому, что появились компьютеры, а наоборот, компьютеры появились, потому что появились программы, которые должны были выполняться людьми.

Код лучше писать с тестами, но так, как будто тестов нет. Тесты должны быть страховкой, а не определяющей основой корректности. ASSERT можно отнести к разновидности тестов.

Некоторые разработчики предполагают наличие дополнительной потенциальной возможности аппаратуры противостоять логическим программным ошибкам[0] за счёт якобы бесплатности встроенных постоянных проверок. Как показывает опыт, программная защита от логических ошибок из-за высокоуровневости не только не уступает аппаратным, но и обладает дополнительным преимуществом за счёт большей гибкости.

Свойства безопасности программы зависят не от целевого языка, в который транслируется код, а от исходного, на котором код пишется. Естественно, если код распространяется на целевом языке без подтверждения происхождения, нет никакой гарантии, что он на самом деле не является и исходным хотя бы отчасти.

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

Языки ассемблера, встроенные в высокоуровневые языки программирования — это свидетельство того, что архитектура приложений пошла по наклонной, и теперь нуждается в очередном костыле, чтобы портить её было удобней.

Вопреки распространённому заблуждению машинные языки и языки ассемблера переносимы. Поэтому даже в тех случаях, когда они всё-таки проникают в интерфейс ОС или смешиваются с высокоуровневым кодом, относиться к ним стоит так же, как и высокоуровневым языкам. То есть код на около-машинных языках позволять исполнять и на несовпадающих архитектурах, так как этого может быть вполне достаточно во многих случаях. А совпадение архитектур — это лишь возможность для менее ресурсоёмкой работы, а не обязательное условие.

Часть программистов гордится тем, что они не просто кодеры, то есть набирающие код в среде программирования, а программисты-проектировщики, размышляющие о чём-то более высоком, в частности об архитектуре. На деле же, архитектура, как и многие другие «высокие» сущности программирования — это тоже код. То, что их записывают на неформальном языке или вообще не записывают вместо того, чтобы оформлять как полноценный код, проверяемый и транслируемый — это провал индустрии, а не повод для гордости.

Классификация — это не столь родовое понятие, сколь внешняя оценка схожих качеств самого предмета. Хотя родственные связи могут быть причиной сходств, ни необходимым, ни достаточным условием этого они не являются. Проблемой многих моделей ООП является то, что они достаточно плохо выражают эту мысль.

Слабостью наследовательного ООП является путаница в определении мощности типа. С одной стороны состав потомка является надмножеством состава предка. С другой стороны, возможные значения ссылочного тип потомка, наоборот, оказываются подмножеством значений, допустимых для предка. Поскольку между значением содержимого объекта и ссылкой на него, часто проводится соответствие, то это приводит к путанице и непониманию, кто от кого должен наследоваться. То ли квадрат от прямоугольника, то ли прямоугольник от квадрата.

Абстракция — ещё одна жертва программистской терминологии. То, что в программировании часто называют абстракцией, на деле является посредничеством, которое не более абстрактно, чем прямая работа. Настоящее абстрагирование всегда приводит к упрощению рассматриваемой модели, а посредничество увеличивает количество деталей. Программистские «абстракции» при чрезмерном увлечении способны как существенно усложнить проект, так и привести к дополнительным накладным расходам [1], что было бы странно для абстракций в исходном смысле.

Это распространённое явление — преподнесение возможности выбора зависимости в качестве «независимости». Вот и в программном обеспечении любят путать платформонезависимость и межплатформенность. Платформонезависимости же для ПО просто не существует. Более того, одним из самых удачных способов воплощения межплатформенности — это предоставление ещё одной платформы, воплощаемой на предлагаемых для исполнения платформах. К примеру, приложения Java на самом деле не платформонезависимы, как о них часто пишут, а зависимы от платформы Java.

Слова «изобретать велосипед» в основном используются для изобличения создания ненужного кода вместо переиспользования общепринятых решений. Это довольно занятно, ведь современные настоящие велосипеды совсем не такие, какимм они были на заре своего использования, и это потому, что настоящие велосипеды как раз таки создаются постоянно. Изобретаются даже новые разновидности, а конкретные новые конструкции преподносятся совсем в огромном количестве. Что до кода, то прямое использование многих распространённых решений несёт с собой такие издержки, что нередко создание более частных решений выглядит куда меньшей проблемой.

Бросая исключение, программист откладывает обработку ошибки до момента, когда будет ясно, что с ней делать, а ловит исключение, как часто бывает, там, где уже совершенно неясно, как эту ошибку хорошо обработать.

Производительность кода всегда важна. Распространённое обратное утверждение делается обычно из-за того, что зависимость проходит через дихотомию достаточности и нехватки. При просмотре ряда задач, для которых производительности достаточно, может создасться впечатление, что зависимости нет. А при просмотре задач, для которых производительности не хватает — что для этих задач просто нужен другой инструмент. Таким нехитрыми интерпретациями и достигается «неважность».

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

Языки только для записи (write-only) – это серьёзное преувеличение. Чтобы записать что-то сравнительно сложное, его требуется перечитывать ещё при написании. Хотя чтение по горячим следам проще, чем неизвестный код, вся сложность от этого не снимается. Если это чтение будет сложно, то и записать толком ничего не получится. Поэтому якобы нечитаемые языки — это, скорее, языки с завышенной кривой обучения как чтению, так и записи.

Одна из распространённых ошибок — это взаимодействие программ через текстовые форматы обмена данных, которые значительно уступают в производительности и безопасности двоичным. Текстовые форматы используются в основном потому, что они более читаемые для человека, что является большой глупостью, ведь для читаемости достаточно преобразовывать данные в текст тогда, когда это нужно. Вместо этого, программы, которым почти всегда нужны двоичные данные, постоянно занимаются непроизводительными преобразованиями в текст и обратно даже тогда, когда человеку смотреть на этот текст не нужно.

Главный недостаток лицензии GPL в том, что она ставит на первое место второстепенные технические детали, а не суть, когда идёт речь о связывании кода. Если обращение к библиотеке кода происходит через отдельно собранную программу-библиотеку, то на тот код, что использует библиотеку, перестают распространяться вирусные требования лицензии. Очевидно, это признание слишком большой суровости этих требований, из-за чего и требуется такое послабление. Но гораздо честнее выглядит LGPL — в ней этого требования просто нет.

...


Примечания

[1] Пример доведения посредничества до абсурда — FizzBuzzEnterpriseEdition

Комментариев нет:

Отправить комментарий