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

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

И это ловушка. Это ловушка на многих уровнях.

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

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

Одна из целей программиста при создании фреймворка — переложить скучную работу на заказчика. Да, заказчик. Обычно все начинается так: какой-то бизнес-отдел хочет, чтобы программа облегчила их жизнь, и их работа включает в себя множество утомительных правил, которые взаимодействуют друг с другом. Обычно это такие правила, как расчет налога с продаж, когда ваша компания находится в Лондоне и вы отправляете посылку из Чикаго в Торонто. Это что-то невероятно скучное и сложное, и ваш клиент, возможно, даже не сможет сказать вам, правильно ли вы все делаете, когда закончите — к концу вам захочется выковырять себе глазные яблоки вилкой. Но, но, но, если бы вы создали структуру, с помощью которой клиенты (также известные как лохи) могли бы сами вводить правила и поддерживать их… вы бы заработали свою зарплату и переложили ужасную работу на кого-то другого. Отличный план… за исключением того, что клиенты никогда этого не делают. Это последняя ловушка. Фреймворки также имеют тенденцию постоянно приобретать функции, пока они не станут специальными языками программирования, что прекрасно ведет к следующей патологии.

Программисты любят изобретать велосипед. Гугл в этом плохо разбирается. Ничто не является «масштабом Google», пока оно не будет переписано как часть гигантской кровосмесительной массы кода, которым является Google. На самом деле есть такое выражение: в Google мы не только изобретаем велосипед, но и вулканизируем собственную резину.

И как.

Проблема с изобретением колес заключается в том, что вновь изобретенные колеса редко бывают круглыми. Обычно бывает так, что есть инструмент, который делает в основном то, что вы хотите, но не все. Таким образом, вместо того, чтобы сделать рациональный вывод: «Я собираюсь смириться с этим и упустить функцию X, чтобы я мог сделать эти 90% за 10 минут» или даже «Я просто используйте это и используйте мою единственную функцию, которая мне нужна, даже если она может быть немного уродливой», мы идем на большие пушки «Я напишу свою собственную версию, которая делает именно то, что мне нужно», которая постепенно превращается в «Я Я напишу свою собственную версию, которая делает то, что мне нужно, что, как я думаю, мне может понадобиться в будущем и что, как я думаю, могут захотеть другие люди», заимствуя из вышесказанного.

Теперь иногда нужно изобретать велосипед. Я не собираюсь это оспаривать. Если вы строите транспортное средство для пересечения ледников, возможно, вы не сможете получить колеса от Canadian Tire. Теперь, если вы живете в Калифорнии и думаете, что вам нужно заново изобретать колеса, чтобы вы могли водить машину в Торонто… ну, нет. Как понять, что нужно что-то заново изобретать? Ты должен плакать и не спать. Вы должны были порыться в Интернете и попробовать десятки альтернатив и посмотреть, есть ли у них фатальный недостаток и можно ли исправить этот фатальный недостаток; после нескольких недель, измученных и уставших от чтения сообщений на форуме, вы должны рухнуть деморализованной кучей с осознанием того, что вам нужно изобретать велосипед. Конечно, миру не нужен еще один парсер аргументов командной строки; на самом деле, учитывая, что getopt(1) находится в библиотеке C, вам никогда не нужно писать ее. Здесь также есть некоторые соображения для ваших пользователей: getopt может быть дерьмовым, но огромная коллекция программ командной строки является ядром UNIX, поэтому, даже если ваши пользователи никогда не слышали о getopt, они найдут интерфейс знакомым. Новинка переоценена. Кроме того, это экономит кучу кода, поэтому вы можете решить проблему, которая вас действительно волнует.

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

Изобретение инструмента — это то, чем программисты часто занимаются. Чего мы не делаем, так это различия между инструментом и приспособлением. В производственном мире, когда вы хотите сделать много одинаковых предметов, есть сильный стимул для создания инструментов, которые помогут вам в этом. Кондуктор - это то, что удерживает детали в правильной ориентации, чтобы существующая машина могла легко, точно и воспроизводимо формировать их. Приспособление может быть простым: доска с несколькими гвоздями, которые удерживают заготовку, и несколько стрелок и меток, где резать. Это то, что держится на кусках дерева, поэтому ваша пила может сделать соответствующий стык под углом. Вы не строите новую пилу, чтобы сделать этот разрез; вы делаете что-то, чтобы расширить возможности этого инструмента для вас.

Кондуктор удерживает детали в правильной ориентации, но он также может удерживать их в неправильной ориентации. Приспособление требует, чтобы оператор полностью знал, как использовать приспособление. На самом деле инструменты также требуют от оператора знания того, что они делают. Когда программисты создают инструменты, они не делают различия между простыми инструментами (приспособлениями) и сложными инструментами. Есть множество простых инструментов, которые не должны мешать пользователю делать глупости. Это выходит за рамки их возможностей. Как только вы поймете, что делаете, они позволят вам повторять это действие много раз с относительной легкостью. Это механические усилители вашей работы. Инструменты получают возможность быть умнее. У инструментов есть функции, которые защищают вас и вашу работу от «плохих вещей».

Я часто вижу это в системе типов Java. Люди хотят использовать универсальный, чтобы попытаться обеспечить некоторое соответствие или правильность. Все, что могут сделать дженерики в Java, — это предотвратить проблемы с кастингом; им не хватает выразительной силы для чего-либо еще. Вы можете создать очень полезную сборку, используя дженерики Java, которые, если кто-то укажет глупые границы типов, будут давать глупые результаты. Точно так же, как приспособление для соединения под углом не помешает вам вставлять куски дерева вверх ногами.

Изобретатели языков программирования любят мешать программистам делать плохие вещи. Реальность такова, что вы не можете. Решение полных по Тьюрингу задач требует создания инструмента с режимами отказа, которые невозможно обнаружить (так называемая проблема остановки). Это нормально. Пила бесполезна, если она не прорезает предметы тверже человека. Это всегда будет опасно. Программисты хотят верить, что можно создать инструмент, который сделает задачу легкой и идеальной каждый раз. Это невозможно. Хорошие инструменты уберегут вас от типичных ошибок, но чтобы быть полезными, они должны позволять вам совершать ошибки.

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

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

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

Любой, кто читал Дзен и искусство обслуживания мотоциклов, знает, что деление системы, даже разработанной, произвольно. Если я могу навсегда переопределить интерфейсы, не меняя систему, как может быть так, что проблемы в системе возникают из-за плохо определенных интерфейсов?

Биологические системы дают интересные ответы, потому что биологические системы часто используют в своих интересах эмерджентное поведение и могут иметь очень стабильную активность, несмотря на очень высокую степень связи между компонентами и отсутствие различимых интерфейсов за пределами электронных орбиталей. Да, даже экосистемы складываются таким образом, что зависят от определенных квантовых состояний. Подумай об этом. Это даже не удивительно. Лучшие системы, которые мы разработали, такие как Интернет, имеют все виды странного эмерджентного поведения, зависящего не только от технологий, но и от вовлеченных людей.

Это сложно понять и отладить. Я не обязательно выступаю за попытку построить систему, основанную на эмерджентном поведении; Я указываю, что это становится неизбежным.

Еще одна маленькая биологическая жемчужина: 100-процентная эффективность — это плохо. Да, нам всем нравится верить, что эффективность — это самое лучшее, что мы можем сделать. Повышение эффективности системы всегда сделает ее лучше, и единственный способ сделать это — повысить эффективность отдельных компонентов. Мы видели много биологических систем с неоптимальными компонентами, где повышение эффективности компонента снижает эффективность системы. Типичным примером является фермент рибулозо-1,5-бисфосфаткарбоксилаза оксигеназа (RuBisCO), компонент механизма фотосинтеза растений. Предполагается, что RuBisCO улавливает углекислый газ, но иногда вместо этого захватывает кислород, что приводит к фотодыханию, которое тратит энергию впустую. Повышение эффективности RuBisCO приводит к уменьшению количества продуктов фотосинтеза на выходе из системы.

Если вы собираетесь построить систему, которая в конечном итоге станет эмерджентной и неэффективной, то кусочно-оптимальная стратегия — проигрышная стратегия. Гораздо лучше пойти на понятную часть.

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