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

(Примечание. Чтобы прочитать это, вам не нужен опыт программирования.)

Трудно знать будущее

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

Вот самая сложная часть. Когда мы пишем законы или кодекс, мы обычно не знаем всех деталей проблемы. Но мы все же должны предоставить хотя бы некоторые приблизительные рекомендации в качестве решения.

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

Что нам тогда делать? Сначала я расскажу о том, как мы с этим справляемся, а затем о программировании.

Проблема в законе

Мы издаем законы для разрешения конфликтов между людьми.

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

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

Banned Vehicle List:
1. Cars
2. Trucks
3. ATVs
4. Motorcycles
...and so on...

Есть проблема. Как бы мы ни старались, неизбежно кто-то воспользуется «транспортным средством», не входящим в наш список. А что насчет сегвеев? Строительные грузовики? Мы обязательно что-то упустим.

Абстрактный подход

Было бы лучше написать общий абстрактный закон, который просто гласит: «Никаких транспортных средств в парке».

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

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

Обратные стороны

У этого подхода есть компромиссы.

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

Еще один недостаток - нам приходится интерпретировать закон через суд. Это требует времени и ресурсов.

Пример

Давайте рассмотрим полный пример того, как это работает на практике. Предположим, служащий парка на своей тележке для гольфа наехал на кого-то в парке. Это была полная случайность - ни грубой игры, ни халатности. Обычно работник не несет ответственности за травму. Однако есть закон, который гласит: «В парке нет транспортных средств». Включает ли это служащих парка, управляющих своими тележками для гольфа? В этом случае ответственность за это будет возложена на сотрудника парка.

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

Но могли ли мы предугадать эту проблему при написании закона? Тяжело сказать.

Урок Закона

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

Проблема в программировании

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

(1) Они могут написать набор конкретных алгоритмов, каждый из которых решает одну проблему за раз.

(2) Или они могут написать один общий алгоритм, который решает все их текущие проблемы, и связанные проблемы, которые могут возникнуть в будущем.

На первый взгляд вариант (2) звучит лучше. Но я уверен, что вы знаете, что все гораздо сложнее.

Когда программисты пишут общие алгоритмы, мы говорим, что они делают свой код более «абстрактным». Когда программисты пишут более абстрактный код, этот код может решать более широкий круг проблем.

Пример

Предположим, программисту нужно рассчитать налог с продаж в Калифорнии. Одним из решений было бы написать алгоритм, который требовал бы в качестве входных данных общую сумму продажи. Затем код умножит эту сумму продажи на налог с продаж Калифорнии (8,5%). Задача решена.

calculateCaliforniaSalesTax(input1: saleAmount) {
    return saleAmount * 0.085
}

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

calculateCaliforniaSalesTax(input1: saleAmount)
calculateUtahSalesTax(input1: saleAmount)
calculateNewYorkSalesTax(input1: saleAmount)
calculateFloridaSalesTax(input1: saleAmount)
...

Но написание всего этого кода (и поддержание его в актуальном состоянии) потребует много работы. И кроме того, откуда мы знаем, что это будет только в США? В какой-то момент ему, вероятно, понадобится налог с продаж для страны, провинции или города, о которых он никогда не слышал.

Абстрактный подход

Вместо того чтобы писать отдельный алгоритм для каждой налоговой ставки, он может написать более абстрактный алгоритм. Он мог сделать это, добавив taxRate вход в алгоритм.

calculateSalesTax(input1: saleAmount, input2: taxRate) {
    return saleAmount * taxRate
}

В нашем исходном алгоритме мы вводили только сумму продажи. Но в абстрактном алгоритме мы вводим и сумму продажи, и ставку налога (например, taxRate для Калифорнии будет 0.085).

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

Обратная сторона

Так в чем же загвоздка на этот раз?

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

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

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

Урок программирования

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

Достижение баланса

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

Давайте посмотрим, как мы можем улучшить закон и код из наших предыдущих примеров.

Изменение Закона о транспортных средствах

Что касается закона о транспортных средствах, мы можем изменить его, добавив несколько конкретных квалификаторов:

«В парке запрещены моторизованные транспортные средства, кроме тех, которые используются сотрудниками парка».

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

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

Изменение алгоритма налога с продаж

Мы можем улучшить алгоритм налога с продаж, сделав этот taxRate ввод менее абстрактным. Вместо того, чтобы позволять другим программистам вставлять любое число для налоговой ставки, мы могли бы потребовать от них ввести вместо этого region (например, california). Затем алгоритм найдет налоговую ставку для этого региона.

caclulateSalesTax(input1: saleAmount, input2: region)

Намного приятнее.

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

Заключение

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

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

Дорожная карта

Часть 2 - Программирование снизу вверх и общее право

Часть 3 - Рефакторинг и реформирование

Часть 4 - Микросервисы, транзакционные издержки и федерализм