Ко мне пришло глубокое осознание того, что большую часть оставшейся мне жизни я потрачу на поиск ошибок в моих же программах.
До сих пор мы говорили об алгоритмах, то есть абстрактных или схематических описаниях процессов, в которых опускаются подробности и практические аспекты. Алгоритм – это точный и недвусмысленный рецепт. Он выражается с помощью базовых операций из фиксированного набора, причем их суть полностью известна и конкретизирована. В нем описана последовательность шагов выполнения данных операций, а также учтены все возможные ситуации, и в конечном счете он гарантированно завершается.
Напротив, программа — что угодно, но только не абстракция. Это конкретное описание каждого шага, который реальный компьютер должен совершить для решения задачи. Различие между алгоритмом и программой подобно различию между чертежом и зданием. Первое – это схема, а второе – настоящий объект.
Другими словами, программа – это один или несколько алгоритмов, выраженных в форме, которую компьютер может обрабатывать непосредственно. На нее влияют такие практические аспекты, как нехватка памяти, ограниченная скорость процессора, неверные или даже вредоносные данные, неисправное оборудование, сбои сетевых подключений и человеческий фактор (он всегда присутствует на заднем плане и часто усугубляет проблемы). Если алгоритм – это обобщенный рецепт, то программа – детальный набор инструкций для робота-повара, который готовит еду на месяц для целой армии, находясь под обстрелом противника.
Конечно, на одних метафорах далеко не уедешь, поэтому мы поговорим о настоящем программировании достаточно обширно, чтобы вы разобрались с этой темой, но не настолько подробно, чтобы вы научились профессионально писать код. Порой программировать тяжело: нужно отладить много мелочей, а самые крошечные недочеты могут привести к появлению серьезных ошибок. Но нет ничего невозможного, и программирование способно по-настоящему увлечь вас. Кроме того, это востребованный навык.
В мире не хватит специалистов, чтобы запрограммировать все, что должны выполнять компьютеры согласно нашим пожеланиям или потребностям. Поэтому одна из вечных тем в области вычислений – то, как задействовать ЭВМ, чтобы они брали на себя всё новые аспекты программирования. Здесь мы подходим к обсуждению ЯП – языков, благодаря которым мы можем в более или менее естественной для человека форме прописать вычислительные шаги, нужные для выполнения задачи.
Помимо того, непросто управлять ресурсами компьютера, особенно учитывая сложность современного оборудования. Поэтому мы также поручаем ЭВМ контроль над ее собственными действиями, что подводит нас к концепции операционных систем (ОС). Тема этой главы – программирование и ЯП, а в следующей мы обсудим программные комплексы, особенно ОС. В главе 7 мы более подробно рассмотрим два важных языка – JavaScript и Python.
Вы определенно можете пропустить подробности синтаксиса в примерах кода из этой главы, однако стоит обратить внимание на сходства и различия в том, как задаются вычисления.
5.1. Ассемблерный (сборочный) язык
Создание программ для первых по-настоящему программируемых ЭВМ требовало немалых трудов. Специалистам приходилось переводить инструкции и данные в двоичные числа, делать их читаемыми для машин, пробивая отверстия в карточках или бумажной ленте, а затем загружать эти носители в память компьютера. Такой процесс невероятно сложен даже для крошечных программ: их изначально тяжело составить правильно, а затем трудно корректировать и изменять, если обнаружится ошибка или возникнет необходимость исправить или добавить данные и инструкции.
То, к каким проблемам это приводило, можно понять по цитате Мориса Уилкса в эпиграфе выше. Уилкс разрабатывал и внедрял EDSAC – один из первых компьютеров с хранимой программой, который начал работать в 1949 году. В 1967 году Морис получил премию Тьюринга за свои заслуги, а в 2000 году его посвятили в рыцари.
В начале 1950-х годов программы создавались для выполнения некоторых простых канцелярских обязанностей. Благодаря им разработчики могли использовать осмысленные названия для инструкций (ADD вместо 5, например) и имена для определенных ячеек памяти (Sum вместо 14). Программа, которая управляет другой программой, – это важная идея, лежащая в основе большинства значимых достижений в области ПО.
Программа, которая выполняет такие операции, называется ассемблером, потому что первоначально она только транслировала, или ассемблировала, любые необходимые части иной программы, написанные ранее другими специалистами. Язык для нее называется ассемблерным, а программирование на данном уровне – программированием на языке ассемблера. В главе 3, когда мы описывали компьютер-игрушку и программировали на нем, мы пользовались именно ассемблерным языком. С помощью ассемблера намного проще изменять программу, потому что, когда программист добавляет или удаляет инструкции, ассемблер отслеживает местоположение каждой инструкции и элемента данных в памяти, то есть специалисту не приходится вести их учет вручную.
Ассемблерный язык (АЯ) специфичен для архитектуры процессоров, для работы с которыми он предназначен. Как правило, он в точности совпадает с инструкциями ЦПУ и «знает», как именно они закодированы в двоичный формат, как информация размещается в памяти и т. д. Это означает, что программа, написанная на АЯ определенного вида процессора (скажем, Intel в Mac или PC), будет отличаться от программы на АЯ для выполнения такой же задачи на другом ЦПУ – например, процессоре ARM в сотовых телефонах. Если кто-то захочет перевести программу с АЯ одного процессора на другой, то ее придется полностью переписать.
Приведем конкретный пример. В компьютере-игрушке, чтобы сложить два числа и сохранить результат в ячейке памяти, потребуются три инструкции:
LOAD X
ADD Y
STORE Z
Они будут одинаковы для множества современных процессоров. Однако в ЦПУ с другим набором инструкций такие же расчеты можно осуществить с помощью последовательности из двух инструкций, которые обращаются к ячейкам памяти без использования накопителя:
COPY X, Z
ADD X, Z
Чтобы переписать программу Игрушки для выполнения на другом компьютере, программист обязан безупречно разбираться в обоих процессорах и тщательно преобразовывать команды из одного набора инструкций в другой. Это тяжелая работа.
5.2. Языки высокого уровня
На рубеже 1950-х и 1960-х годов удалось сделать еще один шаг к тому, чтобы компьютер стал более программируемым. Возможно, этот прорыв стал самым важным в истории программирования. Речь идет о разработке ЯП высокого уровня, которые не зависели от конкретной архитектуры процессора. Они позволяют описывать вычисления в терминах, близких к тому, как выражается человек.
Код, написанный на языке высокого уровня, преобразуется программой-транслятором в инструкции на АЯ конкретного целевого процессора, а они, в свою очередь, переводятся ассемблером в биты для загрузки в память и выполнения. Транслятор обычно называется компилирующей программой (компилятором). Это еще один исторически сложившийся термин, он не особенно содержателен и интуитивно понятен.
На обычном языке высокого уровня вышеописанные расчеты, при которых складываются два числа – X и Y, а результат сохраняется в виде третьего числа – Z, описываются так:
Z = X + Y
Это означает «получить значения из ячеек памяти X и Y, сложить их и сохранить результат в ячейке памяти Z». Оператор «=» означает «заменить» или «сохранить», а не «равно».
Компилирующая программа в Игрушке преобразовала бы это в последовательность из трех инструкций, а в другом компьютере – в две инструкции. Соответствующие ассемблеры затем транслировали бы инструкции на своем языке в фактические наборы битов настоящих инструкций, а также выделили ячейки памяти под величины X, Y и Z. Полученные наборы битов почти наверняка оказались бы различными для каждого из компьютеров.
Схема данного процесса приведена на рис. 5.1, где показано, что одинаковые входные выражения, проходя через две разные компилирующие программы и соответствующие ассемблеры, преобразуются в две различные последовательности инструкций.
Рис. 5.1. Процесс компиляции в двух программах
На практике компилирующая программа делится на «внешний интерфейс», преобразующий программу на языке высокого уровня в некую промежуточную форму, и какое-то количество «внутренних частей», каждая из которых переводит эту общую промежуточную форму на АЯ той или иной архитектуры. Такая организация проще, чем при наличии нескольких полностью независимых компиляторов.
Языки высокого уровня обладают большими преимуществами перед АЯ. Они ближе к образу мышления человека, их проще учить и использовать. Вам не нужно разбираться в наборах инструкций для всех процессоров, чтобы успешно писать код на этих языках. Все это позволяет большему количеству людей создавать программы для компьютеров, причем быстро. Кроме того, программа на языке высокого уровня не зависит от конкретной архитектуры, поэтому ее можно запускать на ЦПУ разных видов, обычно даже без изменений. Достаточно лишь скомпилировать ее с помощью другого компилятора, как показано на рис. 5.1. Программу можно написать только один раз и запускать на различных вычислительных устройствах. Это позволяет амортизировать затраты на разработку программ для всевозможных типов компьютеров – даже для тех, что еще не появились.
На этапе компиляции также обеспечивается предварительная проверка того, нет ли в коде серьезных недочетов, которые программист обязан исправить перед выпуском исполняемой программы. К ним относятся опечатки, ошибки в синтаксисе вроде непарных скобок, операции с неопределенными величинами и так далее. Некоторые из них трудно выявить в программах на АЯ, где каждая последовательность инструкций должна считаться достоверной. (Конечно, даже в синтаксически правильной программе может оставаться множество ошибок, не обнаруженных компьютером.) Важность языков высокого уровня сложно переоценить.
Я покажу одну и ту же программу, написанную на шести наиболее важных языках программирования высокого уровня – Fortran, С, C++, Java, JavaScript и Python, – чтобы вы в общих чертах уяснили их сходства и различия. Каждая из них выполняет то же самое, что и программа для компьютера-игрушки в главе 3. Она складывает целые числа, а когда появляется нулевое значение, выводит сумму и останавливается. Все программы имеют одинаковую структуру: они дают названия используемым величинам, присваивают нулевое начальное значение для суммы, считывают числа и суммируют их, пока не будет введен ноль, затем выводят полученную сумму. Не беспокойтесь о деталях синтаксиса: примеры в основном нужны для того, чтобы вы получили представление, как выглядят языки. Я старался, чтобы образцы программ как можно больше походили друг на друга, хотя, возможно, есть и более удачные способы написать любую из них в отдельности.
Первые языки высокого уровня были сосредоточены на конкретных прикладных областях. Один из самых ранних, FORTRAN, получил название от словосочетания «перевод формул» (англ. Formula Translation,) и сегодня пишется как Fortran. Его разработала команда сотрудников IBM под руководством Джона Бэкуса, и с помощью этого языка очень успешно задавали вычисления в науке и технике. Многие ученые и инженеры (включая меня) изучали Fortran в качестве своего первого ЯП, и по сию пору он жив и здоров. С 1958 года Fortran прошел несколько этапов развития, но сохранил свои узнаваемые черты. Бэкус получил премию Тьюринга в 1977 году в том числе и за работу над этим языком.
На рис. 5.2 приведена программа на языке Fortran для сложения ряда чисел.
Эти инструкции написаны на Fortran 77, и они выглядели бы по-другому в более ранней или поздней версии – например, самой свежей Fortran 2018. Поразмыслите, как перевести арифметические выражения и последовательность операций на АЯ нашей Игрушки. Операции read и write, очевидно, соответствуют GET и PRINT, а четвертая строка – явно проверка IFZERO.
Рис. 5.2. Программа на языке Fortran для сложения чисел
Второй основной язык высокого уровня конца 1950-х – COBOL (Common Business Oriented Language[32]), на который сильно повлияла работа Грейс Хоппер над высокоуровневыми решениями для ассемблерного языка. Хоппер сотрудничала с Говардом Эйкеном при создании «Марк I» и «Марк II» с гарвардской архитектурой, ранних механических компьютеров, а затем над UNI VAC I[33]. Грейс одной из первых разглядела потенциал языков высокого уровня и компиляторов47. COBOL специально предназначался для обработки бизнес-данных, и его языковые особенности позволяли легко задавать структуры данных и вычисления, которые использовались для инвентаризации, подготовки счетов, составления платежных ведомостей и т. п. COBOL продолжает жить: он сильно изменился, но все еще узнаваем. Существует много устаревших программ, написанных на этом языке, однако пишущих на нем программистов мало. В 2020 году в штате Нью-Джерси обнаружили, что старинные программы для обработки заявлений о безработице не справляются с возросшим объемом обращений, вызванным COVID-19, но власти не смогли найти достаточно опытных специалистов для обновления программ на COBOL.
Еще один язык того времени – BASIC (Beginners All-purpose Symbolic Instruction Code – «Универсальная система символического кодирования для начинающих»), разработанный Джоном Кемени и Томом Курцем в 1964 году в городе Дартмут. Предполагалось, что BASIC будет несложным языком для обучения программированию. Он действительно получился очень простым и требовал незначительных вычислительных ресурсов, поэтому оказался первым языком высокого уровня, доступным на персональных компьютерах, когда они только появились. Билл Гейтс и Пол Аллен, основатели Microsoft, начинали с разработки компилятора на BASIC для микрокомпьютера Altair в 1975 году, что стало первым продуктом их компании. На сегодняшний день один из основных видов BASIC все еще активно поддерживается как Microsoft Visual Basic.
Поначалу, когда компьютеры стоили дорого, но считали медленно и умели немногое, существовало опасение, что программы на языке высокого уровня будут неэффективны, поскольку компиляторы не смогут создавать настолько же компактный и практичный код ассемблера, как опытные программисты на АЯ. Однако авторы компиляторов усердно трудились над тем, чтобы их детища выдавали такой же хороший код, как написанный людьми, что помогло внедрению языков. Современные компьютеры работают в миллион раз быстрее, у них вдоволь памяти, и программисты редко беспокоятся об эффективности на уровне отдельных инструкций. Впрочем, для компиляторов и их разработчиков это по-прежнему очень важно.
Fortran, COBOL и BASIC обрели такой успех потому, что сосредоточились на конкретных областях применения и намеренно не пытались решить все возможные задачи программирования. В течение 1970-х годов создавались языки, предназначенные для «системного программирования», то есть написания инструментов программирования – ассемблеров, компиляторов, текстовых редакторов и даже операционных систем. Безусловно, наиболее удачным из этих языков стал С, разработанный в 1973 году Деннисом Ритчи, сотрудником Лабораторий Белла. С тех времен С остается одним из самых популярных и широко используемых ЯП, причем изменился он немного. Современная программа на С очень похожа на те, что написали 30 или 40 лет назад.
На рис. 5.3 приведен очередной сравнительный пример программы для сложения чисел, теперь на С.
Рис. 5.3. Программа на языке С для сложения чисел
1980-е годы ознаменовались разработкой таких языков, как C++ (автор – Бьярне (Бьёрн) Страуструп, также из Лабораторий Белла), цель которых состояла в том, чтобы помочь людям справиться со сложностями при написании очень больших программ. C++ развился из С, и в большинстве случаев программа на С, как на рис. 5.3, годится для использования в программе на C++, но не наоборот.
На рис. 5.4 приведен пример сложения чисел в C++ – один из многочисленных способов решения такой задачи.
Рис. 5.4. Программа на C++ для сложения чисел
Большинство основных программ, которые мы сегодня используем на наших личных компьютерах, написаны на языке С или C++. Я печатаю этот текст на Мас, в котором большинство ПО создано на С, C++ и Objective-C (один из диалектов С). Первый черновик я набирал в Word, программе на языке С и C++, а в настоящее время я редактирую, форматирую и печатаю с помощью программ на С и C++, делаю резервные копии в операционных системах Unix и Linux (они обе – программы на С) и выхожу в Сеть через Firefox, Chrome и Edge (все на C++).
В 1990-е годы началось развитие интернета и Всемирной паутины, что привело к разработке многих новых ЯП. Компьютеры по-прежнему получали все более быстрые процессоры, объемы ОЗУ увеличивались, и потому скорость и удобство программирования стали более важными, чем эффективность использования «железа». Языки вроде Java и JavaScript намеренно идут на этот компромисс.
Java создан в начале 1990-х Джеймсом Гослингом из компании Sun Microsystems. Первоначально этот язык ориентировался на небольшие встроенные системы, такие как бытовая техника и электронные устройства, где прежде всего требовалась гибкость, а не скорость. Затем Java перепрофилировали для запуска на веб-страницах, но там он не прижился, зато широко применяется на вебсерверах. Когда вы посещаете сайт вроде Ebay, на вашем компьютере работают C++ и JavaScript, но вполне вероятно, что интернет-магазин использует Java для подготовки страницы, которую отправляет в ваш браузер. Кроме того, Java – основной язык, на котором пишутся приложения для Android. Этот ЯП проще, чем C++ (хотя развивается в сторону такой же комплексности), но сложнее, чем C. Java также безопаснее, чем C, поскольку устраняет некоторые опасные функции и имеет встроенные механизмы для обработки задач, подверженных ошибкам, – например, управление сложными структурами данных в памяти. По этой причине он также популярен в качестве первого языка на занятиях по программированию.
На рис. 5.5 показана программа сложения чисел на Java. Для нее потребовалось больше слов, чем в других языках, что вполне типично для Java, но ее получилось бы сократить на две-три строки, если бы я объединил несколько вычислений.
Рис. 5.5. Программа на языке Java для сложения чисел
Это поднимает важный общий вопрос о программах и программировании. Всегда есть много способов написать код для выполнения конкретной задачи. В этом смысле программирование похоже на литературное творчество. При написании прозы важны такие показатели, как стиль и эффективность использования языка. Те же факторы имеют значение при создании программы и помогают отделить действительно гениального специалиста от просто хорошего. Поскольку существует много способов задать одно и то же вычисление, то обычно несложно определить, когда одну программу скопировали с другой. На это очень акцентированно обращают внимание в начале любого курса по программированию, однако порой студенты все равно думают, что плагиат можно скрыть, если просто изменить имена переменных или положение строк. Простите, но так ничего не выйдет.
JavaScript – язык из того же широкого семейства, начало которому положил С, хотя у него много уникальных особенностей. Его создал Брендан Эйх из компании Netscape в 1995 году. У JavaScript нет ничего общего с Java, кроме разве что одинаковой части в названии. С самого начала его проектировали для использования в браузере, чтобы обепечить динамические эффекты на вебстраницах, и сегодня почти все они включают в себя код JavaScript. Мы подробнее поговорим об этом ЯП в главе 7, но ради наглядности сравнений версия сложения чисел в JavaScript представлена здесь, на рисунке 5.6.
Рис. 5.6. Программа на JavaScript для сложения чисел
С JavaScript легко экспериментировать. Язык сам по себе простой. Вам даже не нужно загружать компилятор, он встроен в каждый браузер. Результаты ваших вычислений будут видны сразу. Как мы вскоре увидим, вы можете добавить несколько строчек и выложить этот пример кода на какую-нибудь веб-страницу, чтобы им мог пользоваться любой человек в мире.
Перейдем к Python, созданному в 1990 году Гвидо ван Россумом из Centrum Wiskunde & Informatica[34] (CWI) в Амстердаме. Синтаксически он несколько отличается от С, C++, Java и JavaScript, и одна из его наиболее характерных черт – использование отступов вместо скобок для указания групп команд.
С самого начала Python разрабатывался с акцентом на удобочитаемость. Его легко учить, он стал одним из наиболее широко используемых языков, с богатым набором программных библиотек для почти любой мыслимой задачи, связанной с написанием кода. Если бы мне пришлось выбрать только один язык, который я мог бы изучать или преподавать, я бы назвал Python. Подробнее мы поговорим о нем в главе 7, а здесь на рисунке 5.7 представлен вариант программы для суммирования чисел на Python.
Рис. 5.7. Программа на Python для сложения чисел
В каком направлении теперь будут развиваться языки? Предполагаю, что мы продолжим упрощать написание кода, все более обширно задействуя ресурсы компьютера для помощи нам. Мы также и дальше будем развиваться в направлении ЯП, которые более «безопасны» для программистов. Например, язык С – чрезвычайно острый инструмент, и потому при работе с ним довольно легко по невнимательности допустить ошибку, а потом обнаружить ее, когда уже станет слишком поздно – возможно, лишь после того, как ею воспользуются в неблаговидных целях. В более новых ЯП легче предотвратить появление ошибок – или хотя бы выявить их, – но иногда платой за это становится замедление работы и использование большего объема памяти. В большинстве случаев на такой компромисс нужно идти. При этом, безусловно, есть множество иных приложений (например, системы управления в автомобилях, самолетах, космических кораблях и оружии), где крайне важен емкий, быстро выполнимый код. Поэтому высокоэффективные языки вроде С не уйдут в прошлое.
Хотя все ЯП формально равны, то есть их можно использовать для симуляции машины Тьюринга или имитировать их на ней, определенно нельзя сказать, что они одинаково хороши для решения любых задач программирования. Есть огромная разница между написанием программы для управления сложной веб-страницей на JavaScript и программы на C++ для реализации JavaScript-компилятора. Как правило, трудно найти специалиста, который по-настоящему отлично разбирался бы в обеих этих задачах. Опытный профессиональный программист может сносно владеть десятком ЯП и свободно в них ориентироваться, но уровень мастерства в каждом из них у него точно будет разный.
За минувшие годы люди изобрели тысячи языков программирования, пусть даже сейчас широко распространены только менее сотни из них. Почему так много? Как я уже упоминал, каждый язык – это компромисс. В нем сбалансированы такие аспекты, как эффективность, выразительность, безопасность и сложность. Многие ЯП создавались как ответ на очевидные недостатки их предшественников, поэтому в них используются преимущества ретроспективного подхода и возросшей вычислительной мощности, а зачастую сказываются личные вкусы разработчика. Появление новых сфер применения также дает толчок развитию невиданных прежде языков, нацеленных на подобные области.
Что бы ни происходило, ЯП остаются важной и увлекательной частью информатики. Американский лингвист Бенджамин Уорф сказал: «Язык формирует наши способы мышления и определяет то, о чем мы можем думать». Лингвисты до сих пор спорят о том, верно ли это утверждение для естественных языков, но, похоже, оно применимо для искусственных языков, которые мы изобретаем для передачи команд компьютеру.
5.3. Разработка программного обеспечения
Программирование в реальном мире, как правило, ведется в больших масштабах. Стратегия похожа на ту, которую мы используем, когда пишем книгу или беремся еще за какой-нибудь крупный проект: определяем, что делать, начиная с общей спецификации, последовательно разбиваем задачу на всё более мелкие компоненты, а затем работаем над каждым из них по отдельности, следя, чтобы они сочетались друг с другом. В программировании эти части обычно имеют такой размер, чтобы написать для них точные вычислительные шаги на каком-либо языке получилось у одного человека. Обеспечить совместную работу фрагментов, разработанных разными программистами, довольно сложно, а неудачная реализация данного этапа приводит к многочисленным ошибкам. Например, орбитальный аппарат NASA Mars Climate Orbiter потерпел неудачу в 1999 году, потому что в ПО системы управления полетом для определения тяги использовались метрические единицы, а данные коррекции курса ввели в британских (неметрических) единицах. Из-за этого аппарат вышел на неверную траекторию, слишком приблизился к поверхности планеты и развалился в атмосфере48.
Чтобы познакомить вас с различными ЯП, я приводил выше примеры, состоящие, как правило, из менее чем десяти строк кода. Небольшие программы, которые пишут на любом вводном курсе по программированию, содержат от нескольких десятков до нескольких сотен строк. Первая написанная мною «настоящая» программа (в том смысле, что ею пользовалось значительное количество других людей) насчитывала примерно тысячу строк на языке Fortran. Она работала как простой текстовый редактор для форматирования и печати моей дипломной работы. Затем программу взяла себе одна студенческая организация, которая применяла ее около пяти лет после того, как я окончил университет. Эх, старые добрые времена!
Более серьезная современная программа для выполнения полезной задачи может содержать от тысяч до десятков тысяч строк. В рамках моего курса студенты, трудясь над проектами в малых группах, обычно успевают написать две или три тысячи строк за 8-10 недель, включая время на разработку системы и изучение одного-двух новых языков, причем они также посещают другие курсы и свои внеклассные занятия. Получившийся у них продукт чаще всего представляет собой веб-сервис для легкого доступа к какой-нибудь университетской базе данных или мобильное приложение для социальной жизни.
Для сравнения: компилятор или веб-браузер могут содержать от сотен тысяч до миллиона строк. Наконец, крупные системы состоят из нескольких миллионов или даже десятков миллионов строчек кода, над ними одновременно работают сотни или тысячи людей, а срок их службы измеряется десятилетиями. Компании обычно осторожничают и стараются не раскрывать, насколько масштабны их программы, но достоверная информация иногда всплывает. Например, согласно презентации на Google Conference в 2015 году, в общей сложности в их поисковике насчитывалось около двух миллиардов строк кода49. К настоящему времени, вероятно, их стало минимум вдвое больше.
Для поддержания ПО такого уровня требуется команда программистов, тестировщиков и авторов документации. Для них необходимо определить расписание, сроки сдачи кода и управленческие уровни, а также проводить бесконечные совещания, чтобы ничего не сломалось. Один мой сведущий коллега некогда утверждал, что на каждую строку кода крупной системы, с которой он работал, приходилась одна летучка. Поскольку в той системе содержалось несколько миллионов строчек, возможно, он преувеличивал. Но опытные программисты здесь скажут: «Ненамного».
5.3.1. Библиотеки, интерфейсы и средства разработки
Если сегодня вы начнете строить дом, то не станете валить на пиломатериал деревья и заготавливать глину, чтобы самостоятельно налепить кирпичей. Вместо этого вы купите готовые двери, окна, сантехнику, котел отопления и водонагреватель. Строительство дома по-прежнему будет сложным делом, но с ним можно справиться, поскольку вы сможете опереться на то, что уже сделали другие, и положиться на готовую инфраструктуру – фактически, на помощь целой индустрии.
То же верно и в отношении программирования. Серьезных программ, которые бы создавались с нуля, практически не существует. Можно взять «с полочки» много компонентов, уже написанных кем-то другим, и использовать их напрямую. Например, если вы пишете программу для Windows или Мас, то у вас уже есть доступ к коду стандартных меню, кнопок, графических вычислений, сетевых соединений, подключений к базам данных и так далее. Большая часть работы заключается в том, чтобы разобраться в этих компонентах и склеить их по-своему. Естественно, такие элементы опираются на другие, более простые и базовые, часто в несколько слоев. Ну, а фундаментом служит ОС – программа, которая управляет оборудованием и контролирует все, что происходит. Мы поговорим об операционных системах в следующей главе.
Проще говоря, ЯП обеспечивают функциональный механизм, с помощью которого один программист пишет код для выполнения полезной операции, а затем упаковывает его в такую форму, что другой разработчик может использовать этот код в своей программе, даже не зная, как именно он действует. Например, программа на языке С, приведенная несколько страниц назад, содержит такие строки:
Этот код «вызывает» (то есть использует) две функции языка С: scanf считывает данные из источника ввода, то есть он аналогичен GET в компьютере-игрушке, a printf печатает выходные данные, как и PRINT. У любой функции есть имя и набор входных значений данных, которые нужны ей для выполнения своей работы. Она производит вычисления и может возвращать результат той части программы, где она задействована. Синтаксис и другие детали здесь специфичны для С и будут отличаться в другом языке, но сама концепция универсальна. Наличие функций позволяет писать программу, опираясь на компоненты, которые уже созданы по отдельности и используются всеми программистами при необходимости.
Набор связанных функций обычно называется библиотекой. Например, С имеет стандартную библиотеку функций для чтения и записи данных на диски и в другие места, причем scanf и printf входят в нее.
Сервисы, предоставляемые библиотекой функций, описываются для программистов в терминах API (Application Programming Interface), то есть программного интерфейса прикладных задач. В нем перечисляются сами функции и то, что они выполняют, указывается, как их использовать в программе, какие входные данные требуются и какие значения они выдают. В API также могут описываться структуры данных (организация данных, передающихся назад и вперед) и еще всякая всячина, которая совместно определяет, как именно программист должен обращаться к сервисам и что будет вычислено в качестве результата. Здесь нужна подробная и точная спецификация, потому что в конечном счете программу будет интерпретировать тупой прямолинейный компьютер, а не дружелюбный и сговорчивый человек.
API включает в себя не только описание синтаксических требований, но и вспомогательную документацию, благодаря которой программисты эффективно используют систему. Современные крупные комплексы часто включают в себя SDK (Software Development Kit), то есть пакет средств разработки ПО, чтобы люди могли ориентироваться во все более замысловатых программных библиотеках. Например, Apple предоставляет среду и инструменты поддержки для программистов, которые пишут код для iPhone и iPad. Google обеспечивает аналогичный SDK для телефонов на Android, a Microsoft – различные среды разработки для создания кода Windows на разных языках для многих устройств. SDK и сами по себе – большие программные комплексы. В частности, Android Studio, пакет средств для этой ОС, весит 1,6 Гб, a Xcode, SDK для разработчиков Apple, – намного больше.
5.3.2. Ошибки (баги)
К сожалению, ни одна серьезная программа не начинает работать с первого раза: жизнь слишком сложна, и программы это тоже отражают. Они требуют безукоризненного внимания к мелочам, которым мало кто обладает. Ошибки встречаются в программах любого масштаба: при определенных обстоятельствах они будут что-то делать неправильно или выдавать неверные ответы. Эти недостатки называются «багами», то есть «жуками», и первое употребление термина часто приписывают Грейс Хоппер, которая упоминалась выше. В 1947 году ее коллеги обнаружили настоящего жука (дохлую моль) в механическом компьютере «Марк II», с которым они работали, и Хоппер, по-видимому, сказала, что они «очищали [машину] от жуков» (англ, debugging), то есть занимались отладкой. Насекомое сохранили, и оно обрело своего рода бессмертие. Его можно увидеть в Музее американской истории Смитсоновского института в Вашингтоне и на фотографии, приведенной на рис. 5.8.
Рис. 5.8. Жучок из ЭВМ «Марк II» гарвардской архитектуры50
Однако Хоппер не вводила новое значение для слова bug; оно датируется 1889 годом. В Оксфордском словаре английского языка (второе издание) сказано:
bug. Дефект или неисправность в машине, плане или подобном.
Происх. США
1889 Pall Mall Gaz[35], 11 марта. 1/1 Мистер Эдисон, как мне сообщили, две предыдущие ночи не спал, обнаружив в своем фонографе «жука» – выражение, обозначающее попытки решить затруднение и подразумевающее, что какое-то воображаемое насекомое спряталось внутри и порождает все неприятности.
Возможные ошибки настолько многотипны, что для описания их видов потребовался бы целый том (и такие книги действительно существуют). Среди их бесконечного разнообразия есть такие: забыть разобраться с возникшим случаем; написать неправильный логический или арифметический тест для оценки какого-либо условия; использовать неправильную формулу; осуществить доступ к памяти за пределами диапазона, выделенного программе или ее части; применить неправильную операцию к определенному виду данных и не добавить проверку пользовательского ввода.
Рис. 5.9. Функции для пересчета между шкалами Цельсия и Фаренгейта
В качестве примера с выдуманной ситуацией на рис. 5.9 показана пара функций JavaScript, которые переводят температуру по Цельсию в градусы Фаренгейта и наоборот. (Операторы * и ⁄ выполняют умножение и деление соответственно.) Одна из этих функций выдает ошибку. Вы заметите ее? Я вернусь к ней через мгновение.
Важная часть реального программирования – тестирование. Компании-разработчики ПО, надеясь выявить как можно больше ошибок перед отправкой продукта пользователям, часто пишут больше тестов, чем кода, и у них работает больше тестировщиков, чем программистов. В целом можно достичь состояния, когда ошибки встречаются хотя бы нечасто, пусть это и сложно.
Как бы вы проверили функции преобразования градусов, показанные на рис. 5.9? Безусловно, вы решите провести несколько простых тестов с ситуациями, в которых ответы известны – например, температура по Цельсию 0° и 100° соответствует значениям 32° и 212° по Фаренгейту. Все работает прекрасно.
Но в другом направлении, от Фаренгейта к Цельсию, программа вычисляет не так хорошо: функция сообщает, что 32 °F равно -14,2 °C, а 212 °F равно 85,8 °C, что неверно. Ошибка в том, что там не поставлены круглые скобки: нужно сначала вычесть 32 из значения по Фаренгейту, а уже потом умножить результат на 5/9. Выражение в f toe должно быть таким:
return 5/9 * (f – 32);
К счастью, эту функцию легко проверить, но только представьте, как много работы может потребоваться для тестирования и отладки программы с миллионом строчек, когда сбои не так очевидны.
Между прочим, эти две функции обратны друг другу (как 2п и log п), что частично упрощает тестирование. Если вы поочередно пропустите любое значение через каждую из них, то в результате получите исходное число (за исключением, возможно, крошечного расхождения, вызванного тем, что компьютер выводит нецелочисленные значения с погрешностью).
Из-за ошибок в ПО система может стать не защищенной от атак, поскольку злоумышленники часто прибегают к уязвимостям, чтобы записывать в память свой вредоносный код. На таких черных ходах зиждется целый рынок: «хорошие» хакеры устраняют проблемы, «плохие» пользуются ими. Посередине находится серая зона, где правительственные органы, такие как АНБ, накапливают «эксплойты», чтобы позже использовать или исправить их.
Тот факт, что уязвимости широко распространены, объясняет, почему так часто обновляются важные программы вроде браузеров, которые находятся в центре внимания многих хакеров. Написать надежную программу непросто, и плохие парни всегда ищут лазейки. Вот почему рядовому пользователю важно обновлять программы по мере того, как их создатели латают прорехи в безопасности51.
Еще одна сложность реального ПО заключается в том, что среда постоянно меняется и программы нужно адаптировать. Разрабатывается новое аппаратное обеспечение, и для него необходимо новое ПО, иногда с изменениями в системах. Также на техзадания программ влияют принятые властями законы и другие требования. Например, пакеты вроде TurboTax[36] должны обновляться в соответствии с частыми изменениями в налоговом законодательстве во многих юрисдикциях. Компьютеры, средства разработки, языки и физические устройства устаревают и подлежат замене. Также выходят из обращения форматы данных: например, в современной версии Word невозможно прочитать файлы начала 1990-х. Вместе с ними исчезают знания и навыки, потому что люди уходят на пенсию, умирают или теряют работу в результате сокращения штата корпорации. С программами, написанными студентами, возникают такие же сложности, когда их авторы покидают стены университета.
Отслеживание постоянных изменений – немалая часть обслуживания ПО. Но ее необходимо делать, иначе программа начнет страдать от «распада битов» (деградации данных) и через какое-то время совсем перестанет работать или обновляться, потому что либо ее нельзя будет перекомпилировать, либо окажется, что одна из библиотек значительно перестроена. С другой стороны, когда вы стараетесь устранять проблемы или расширять функциональность, могут появиться новые ошибки, или же программа станет работать не так, как уже привыкли пользователи.
5.4. Интеллектуальная собственность
Термин «интеллектуальная собственность» относится к различным видам нематериальной собственности, которые появились в результате творческих усилий, таких как изобретение или авторство. Это книги, музыка, картины, фотографии (сами произведения, а не их воплощения на носителях). Важный пример – программное обеспечение. Оно неосязаемо, но ценно. Для создания и поддержания значительного объема кода необходимо много и напряженно работать. В то же время ПО можно копировать неограниченное количество раз и без затрат распространять по всему миру, его легко изменять, и, в конечном счете, оно невидимо.
Концепция прав на программное обеспечение порождает сложные юридические вопросы (думаю, даже в большей степени, чем с аппаратным обеспечением, хотя, возможно, это мое предубеждение как программиста). ПО – более новая область по сравнению с оборудованием, ведь до 1950 года программных средств не существовало, и только за последние лет сорок они стали крупным независимым сектором экономики. Прошло сравнительно мало времени, чтобы законы, коммерческая практика и социальные нормы успели развиться как должно. В этом разделе я рассмотрю некоторые проблемы, чтобы у вы разобрались в основных технических принципах и смогли, по крайней мере, оценить ситуацию с разных точек зрения. Я описываю ситуацию с позиции законодательства США – в других странах есть аналогичные системы, но они отличаются во многих отношениях.
К программному обеспечению с разной степенью успеха применяются несколько правовых механизмов защиты интеллектуальной собственности. К ним относятся коммерческая тайна, товарные знаки, авторские права, патенты и лицензии.
5.4.1. Коммерческая тайна
Коммерческая тайна — наиболее очевидное понятие. Владелец либо хранит в секрете свою собственность, либо открывает ее другим только по юридически обязывающему контракту, вроде соглашения о неразглашении. Это простой и часто эффективный механизм, но он практически не дает возможности обратиться к каким-либо мерам защиты, если секрет когда-нибудь будет раскрыт. Классический пример коммерческой тайны, но из другой области, – рецепт Coca-Cola. Теоретически, если бы тайна стала общественным достоянием, любой человек сумел бы изготовить идентичный продукт, но не смог бы назвать его Coca-Cola или Соке, поскольку это товарные знаки, то есть еще одна форма интеллектуальной собственности. В программном обеспечении примером коммерческой тайны будет код для таких крупных пакетов, как PowerPoint или Photoshop.
5.4.2. Товарный знак
Товарный знак — это слово, фраза, название, логотип или даже особенный цвет, который отличает товары или услуги компании. Например, плавный, будто рукописный шрифт, который используется в рекламе Coca-Cola, и форма классической бутылки Соке – это все относится к товарному знаку. Золотые арки McDonald’s – это торговая марка, которая отличает его от других компаний быстрого питания.
В вычислительной технике существует множество товарных знаков – например, блестящий контур на ноутбуках Mac от Apple. Четырехцветный логотип в операционной системе Microsoft на компьютерах и игровых контроллерах выполняет такую же функцию.
5.4.3. Авторское право
Авторское право (АП) защищает творческое самовыражение, и оно знакомо нам в контексте литературы, искусства, музыки и фильмов. Оно оберегает творческую работу от копирования другими людьми – по крайней мере, в теории – и предоставляет создателям право получать доход от своей работы в течение ограниченного периода времени. В США он раньше составлял 28 лет с одним продлением, но сейчас рассчитывается как «до конца жизни автора плюс 70 лет». Во многих других странах добавляют 50 лет вместо семидесяти. В 2003 году Верховный суд США постановил, что 70 лет после смерти автора – «ограниченный» срок52. Формально это правильно, но на практике не отличается от «навсегда». Правообладатели в Штатах прилагают все усилия, чтобы распространить по всему миру условия авторского права, соответствующие законодательству США.
Обеспечить соблюдение АП на цифровые материалы непросто. Можно без каких-либо затрат создать и распространить по всему миру любое количество электронных копий. Попытки защитить авторские материалы с помощью шифрования и других форм управления цифровыми правами[37], или DRM (digital rights management), неизменно терпели неудачу. Шифрование обычно оказывается уязвимым, и даже если нет, материалы могут перезаписать в ходе их воспроизведения (т. н. аналоговая брешь), например путем тайной съемки в кинотеатре. Частным лицам и даже крупным организациям трудно добиться успеха, преследуя нарушение авторских прав в судебном порядке. Я вернусь к этой теме в главе 9.
Авторское право также распространяется на программы. Если я написал код, то владею им точно так же, как если бы написал роман. Никто другой не может использовать мою защищенную авторским правом программу без моего разрешения. Звучит довольно просто, но дьявол всегда кроется в деталях. Если вы изучите работу моей программы и создадите свою версию, то при каком уровне схожести получится, что она нарушает мои АП? Если вы измените форматирование и имена всех переменных, то это все равно несоблюдение моих прав. Однако в случае более искусных правок все не так очевидно, и нарушение удастся доказать только в результате дорогостоящего юридического процесса. Но если вы изучите поведение моей программы, как следует разберетесь в нем и затем напишете по-настоящему новую реализацию, то она может оказаться законной. На практике применяется метод, называемый разработкой в «чистой комнате» (отсылка к изготовлению интегральных схем), когда принимаются меры, чтобы программисты, стремящиеся воспроизвести функции какого-либо приложения, не имели доступа ни к его коду, ни к сведениям о нем. Они пишут новый код, который действует аналогично оригинальному, но демонстрируется, что его не копировали. С юридической точки зрения тут все сводится к доказательству того, что работа велась в действительно «чистой» комнате и никто не «запачкался», то есть не видел оригинальный код.
5.4.4. Патент
Патенты обеспечивают правовую защиту изобретений. Они отличаются от авторского права, которое распространяется только на представление – то есть на то, как написан код, без учета оригинальных идей, которые он может содержать. Существуют горы патентов на оборудование, такое как хлопкоочистительная машина, телефон, транзистор, лазер, и, конечно же, на множество процессов, устройств и улучшений в них.
Изначально на ПО – алгоритмы и программы – не выдавались патенты, поскольку считалось, что они относятся к «математическим методам» и не попадают под действие соответствующего закона. Как программист со скромным образованием в области матанализа, я не считаю, что алгоритмы – это математика, хотя она в них используется. (Вспомните Quicksort, который в наши дни вполне могли бы запатентовать.) Другая точка зрения заключается в том, что многие патентные описания программных средств слишком тривиальны. Условное «использование компьютера для выполнения какого-то простого или хорошо известного процесса» не должно получать патент, так как здесь не хватает оригинальности. Я во многом согласен с такой позицией, хотя, повторюсь, я не специалист и уж точно не юрист.
Наглядным примером патента для ПО может выступать технология Amazon «Один клик». В сентябре 1999 года четыре изобретателя из Amazon.com, включая Джеффа Безоса, основателя и генерального директора компании, получили патент США № 5 960 411. Его выдали на «Метод и систему размещения заказа для покупки товара через интернет», где заявленное нововведение позволяло зарегистрированным пользователям размещать заказ одним щелчком мыши (рис. 5.10)53. Обратите внимание, что «Один клик» – зарегистрированная торговая марка Amazon, обозначаемая как 1-Click®.
Рис. 5.10. Amazon 1-Click®
Из-за патента на «Один клик» почти 20 лет шли споры и юридические разбирательства. Справедливо сказать, что большинство программистов считают такую концепцию очевидной, однако закон гласит, что изобретение должно выглядеть «неочевидным» для «человека, обладающего обычными навыками в данной области» на момент изобретения, которое случилось в 1997 году, на заре веб-коммерции. Патентное ведомство США отклонило некоторые патентные притязания, но удовлетворило другие. Тем временем патент лицензировали другие компании, включая Apple с ее интернет-магазином iTunes, a Amazon получил судебное постановление, которое запрещало прочим фирмам использовать идею «Один клик» без разрешения. Естественно, в зарубежных странах складывалась иная ситуация. К счастью, все это больше не актуально, поскольку срок патента, составлявший 20 лет, уже истек.
Одним из недостатков легкого получения патентов на ПО стало появление так называемых патентных троллей, или, говоря менее уничижительно, «непрактикующих субъектов»54. Они получают патенты не для того, чтобы использовать изобретение, а с целью судиться с теми, кто якобы нарушает их права. Иск обычно подается в местах, где суды чаще отдают предпочтение инициирующей стороне, то есть троллю. Прямые издержки патентных разбирательств в суде обходятся дорого, а потенциальные затраты в случае проигрыша дела очень высоки. Это особенно актуально для небольших компаний, которым проще и безопаснее уступить и платить лицензионный сбор троллю, даже если патентные притязания слабы и неясно, в чем состоит нарушение.
Правовой климат меняется, хоть и медленно, и со временем патентная деятельность такого рода может ослабнуть. Но пока она остается серьезной проблемой.
5.4.5. Лицензии
Лицензии — это юридические соглашения, по которым дается разрешение на использование продукта. Каждый пользователь компьютера знаком с этапом процесса установки новой версии какого-либо ПО – «Лицензионным соглашением с конечным пользователем», или EULA (End User License Agreement). В диалоговом блоке показывается маленькое окошко с колоссальным объемом текста мелким шрифтом – юридический документ, с условиями которого вы должны согласиться, чтобы двинуться дальше. Большинство людей просто ставят галочку, не читая, что в теории и, возможно, на практике связывает их законными обязательствами по соглашению.
Если вы все-таки прочтете эти условия, то вряд ли удивитесь, увидев, что они односторонние. Поставщик отказывается от любых гарантий и ответственности, даже не обещая, что ПО будет что-то делать. Приведенный ниже отрывок (все буквы прописные, как в оригинале) – небольшая часть лицензионного соглашения для macOS Mojave, операционной системы, работающей на моем Мас55.
B. ВЫ ПРЯМО ПРИЗНАЕТЕ И СОГЛАШАЕТЕСЬ С ТЕМ, ЧТО, НАСКОЛЬКО ЭТО РАЗРЕШЕНО ПРИМЕНИМЫМ ЗАКОНОДАТЕЛЬСТВОМ, ИСПОЛЬЗОВАНИЕ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ APPLE И ЛЮБЫХ УСЛУГ, ПРЕДОСТАВЛЯЕМЫХ ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ APPLE ИЛИ ДОСТУПНЫХ ЧЕРЕЗ НЕГО, ОСУЩЕСТВЛЯЕТСЯ ИСКЛЮЧИТЕЛЬНО НА ВАШ СТРАХ И РИСК И ЧТО ВЕСЬ РИСК В ОТНОШЕНИИ УДОВЛЕТВОРИТЕЛЬНОГО КАЧЕСТВА, ПРОИЗВОДИТЕЛЬНОСТИ, ТОЧНОСТИ И ПРИЛОЖЕННЫХ УСИЛИЙ ЛЕЖИТ НА ВАС.
C. В МАКСИМАЛЬНОЙ СТЕПЕНИ, РАЗРЕШЕННОЙ ПРИМЕНИМЫМ ЗАКОНОДАТЕЛЬСТВОМ, ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ И УСЛУГИ APPLE ПРЕДОСТАВЛЯЮТСЯ «КАК ЕСТЬ» И «ПО НАЛИЧИЮ», СО ВСЕМИ ОШИБКАМИ И БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, И КОМПАНИЯ APPLE И ЕЕ ЛИЦЕНЗИАРЫ (СОВМЕСТНО ИМЕНУЕМЫЕ «APPLE» В РАМКАХ РАЗДЕЛОВ 7 И 8) НАСТОЯЩИМ ОТКАЗЫВАЮТСЯ ОТ ВСЕХ ГАРАНТИЙ И УСЛОВИЙ В ОТНОШЕНИИ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ И УСЛУГ APPLE, ЯВНЫХ, ПОДРАЗУМЕВАЕМЫХ ИЛИ ПРЕДУСМОТРЕННЫХ ЗАКОНОМ, ВКЛЮЧАЯ ПОДРАЗУМЕВАЕМЫЕ ГАРАНТИИИ И/ИЛИ УСЛОВИЯ ТОВАРНОЙ ПРИГОДНОСТИ, УДОВЛЕТВОРИТЕЛЬНОГО КАЧЕСТВА, ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННОЙ ЦЕЛИ, ТОЧНОСТИ, СПОКОЙНОГО ИСПОЛЬЗОВАНИЯ И ОТСУТСТВИЯ НАРУШЕНИЯ ПРАВ ТРЕТЬИХ ЛИЦ, НО НЕ ОГРАНИЧИВАЯСЬ ЭТИМИ ГАРАНТИЯМИ.
D. APPLE НЕ ГАРАНТИРУЕТ ОТСУТСТВИЕ ВМЕШАТЕЛЬСТВА В ВАШЕ ПОЛЬЗОВАНИЕ ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ И СЕРВИСАМИ APPLE И ТО, ЧТО СОДЕРЖАЩИЕСЯ ФУНКЦИИ ИЛИ УСЛУГИ, СОЗДАННЫЕ ИЛИ ПРЕДОСТАВЛЯЕМЫЕ ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ APPLE, БУДУТ СООТВЕТСТВОВАТЬ ВАШИМ ТРЕБОВАНИЯМ, И ЧТО РАБОТА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ СЕРВИСОВ APPLE БУДЕТ БЕСПЕРЕБОЙНОЙ ИЛИ БЕЗОШИБОЧНОЙ, ЧТО ЛЮБЫЕ УСЛУГИ БУДУТ ПРОДОЛЖАТЬ ПРЕДОСТАВЛЯТЬСЯ, ЧТО ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ИЛИ СЕРВИСЫ APPLE БУДУТ СОВМЕСТИМЫ ИЛИ БУДУТ РАБОТАТЬ С ЛЮБЫМ ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ, ПРИЛОЖЕНИЯМИ ИЛИ СЕРВИСАМИ ТРЕТЬИХ СТОРОН, ИЛИ ЧТО ДЕФЕКТЫ В ПРОГРАММНОМ ОБЕСПЕЧЕНИИ ИЛИ СЕРВИСАХ APPLE БУДУТ ИСПРАВЛЕНЫ. УСТАНОВКА ЭТОГО ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ APPLE МОЖЕТ ПОВЛИЯТЬ НА ДОСТУПНОСТЬ И УДОБСТВО ИСПОЛЬЗОВАНИЯ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ, ПРИЛОЖЕНИЙ ИЛИ СЛУЖБ ТРЕТЬИХ СТОРОН, А ТАКЖЕ ПРОДУКТОВ И СЕРВИСОВ APPLE.
В большинстве EULA говорится, что вы не сможете подать в суд на возмещение ущерба, если ПО причинит вам вред. Существуют условия относительно того, для чего может использоваться программное обеспечение, и вы соглашаетесь с тем, что не будете пытаться декомпилировать его или транслировать на ассемблерный язык. Вы не имеете права поставлять его в определенные страны и применять для разработки ядерного оружия (я серьезно)56. Мои друзья-юристы говорят, что такие лицензии, как правило, действительны и подлежат исполнению, если условия не слишком безумны. Но тогда возникает вопрос, как определить их разумность.
Другое положение может немного удивить, особенно если вы покупали ПО в обычном или онлайн-магазине: «Это программное средство лицензировано, а не продано». В отношении большинства покупок юридическая доктрина «первой продажи» гласит, что после приобретения чего-либо вы становитесь владельцем этого. Если вы купили печатную книгу, то теперь экземпляр ваш, и вы можете отдать или перепродать его кому-то другому – хотя, конечно, вам нельзя нарушать авторские права, создавая и распространяя копии издания. Но поставщики цифровых товаров почти всегда «продают» их по лицензии, которая позволяет им сохранять право владения и ограничивать действия с «вашей» копией.
Отличный пример такой ситуации появился в июле 2009 года. Компания Amazon «продает» много книг на своем устройстве для чтения Kindle, хотя в действительности произведения лицензируются, а не продаются. В какой-то момент компания поняла, что распространяла кое-какие книги, на которые не имела необходимых прав, и поэтому «отменила их продажу», то есть отключила на всех устройствах Kindle. По замечательной иронии судьбы, в число отозванных произведений вошло издание антиутопического романа Джорджа Оруэлла «1984». Не сомневаюсь, ему понравилась бы эта история.
Из-за API (прикладной интерфейс программирования) также возникает несколько интересных юридических вопросов, в основном связанных с авторскими правами. Предположим, что я произвожу какую-нибудь программируемую игровую систему, аналогичную ХЬох или PlayStation. Я хочу, чтобы люди покупали мои приставки, и продажи будут успешнее, если на них выйдет много хороших игр. Очевидно, мне не удастся написать всё ПО самостоятельно, поэтому я тщательно продумаю подходящий API, чтобы другие люди сумели создавать проекты для моей консоли. Чтобы помочь разработчикам игр, я могу также предоставить средства разработки ПО, или SDK, аналогичные XDK для ХЬох от Microsoft. Если повезет, я продам горы приставок, заработаю кучу денег и уйду на пенсию счастливым.
API – это, по сути, контракт между пользователем сервиса и поставщиком услуг. Он определяет, что происходит по обе стороны интерфейса: не детали того, как все реализовано, но что именно делает каждая функция при использовании в программе. Это означает, что кто-то еще – например, конкуренты – тоже может сыграть роль поставщика, создав альтернативную консоль, которая предоставляет такой же API, как у меня. Если они прибегнут к методу «чистой комнаты», то это гарантирует, что они никоим образом не скопировали мою реализацию. Если конкуренты все сделают хорошо, то есть все будет работать одинаково, сама приставка окажется лучше моей по каким-то иным характеристикам (например, дешевле или с более шикарным дизайном), и мне придется уйти из бизнеса. Прощайте, мои надежды на обогащение!
Как мне поступить с точки зрения закона? Я не могу запатентовать API, поскольку это не оригинальная идея. И он не будет коммерческой тайной, я ведь должен показывать интерфейс людям, которые станут им пользоваться. Однако если определять API как творческий акт, я должен иметь возможность защитить его авторскими правами, чтобы другим людям, желающим воспользоваться им, требовалась лицензия от меня. То же самое верно, если я предоставляю SDK. Достаточно ли такой защиты? Эта юридическая коллизия и множество подобных ей не вполне разрешены.
Статус авторских прав на API – не гипотетический вопрос. Например, в январе 2010 года компания Oracle купила Sun Microsystems, которая создала язык программирования Java, а в августе 2010 года подала в суд на Google, утверждая, что Google незаконно использует Java API на телефонах с ОС Android, запускающих Java-код57.
Если пересказать суть этой запутанной истории простыми словами, то в итоге окружной суд постановил, что API не может охраняться авторским правом. Oracle подала апелляцию, и решение отменили. Затем Google подала петицию в Верховный суд США, прося о рассмотрении этого дела, но в июне 2015 года получила отказ. В следующем раунде Oracle запросила компенсацию в размере более 9 миллиардов долларов, но присяжные решили, что Google «добросовестно использовал» API и, следовательно, не нарушил закона об авторском праве. Я думаю, что в данном случае большинство программистов на стороне Google, но вопрос еще не решен. (В качестве ремарки скажу, что я дважды подписывал экспертное заключение от EFF[38] (Фонд электронных рубежей, борется с нарушением конфиденциальности с помощью электронных технологий), где поддерживается позиция Google.) После еще нескольких раундов разбирательств Верховный суд вновь рассмотрел дело в октябре 2020 года[39].
5.5. Стандарты
Стандарт — это точное и подробное описание того, как следует создавать какой-либо продукт разработки или как он должен функционировать. Некоторые из них, как. doc и. docx, форматы файлов Word, это стандарты де-факто: они не имеют официального статуса, но все ими пользуются. Слово «стандарт» лучше всего подходит для формальных описаний, которые часто составляет и поддерживает квазинейтральная сторона вроде некоего правительственного агентства или консорциума. В них определяется, как что-либо собрано или функционирует. Там приводится достаточно полное и точное определение, на основании которого отдельные субъекты могут взаимодействовать или независимо предоставлять варианты реализации.
Стандарты аппаратного обеспечения постоянно приносят нам пользу, хотя мы, возможно, не замечаем, как их много. Купив новый телевизор, я могу подключить его к электрическим розеткам в моем доме благодаря стандартам, касающимся размера и формы штекеров, а также напряжения, которое они обеспечивают. (Хотя, конечно, в других странах все иначе; уезжая в Европу, мне приходится брать с собой несколько хитроумных адаптеров, чтобы через них вставлять североамериканские вилки в разные розетки в Англии и Франции.) Сам телевизор будет принимать сигналы и выводить изображения, потому что существуют стандарты широковещательного и кабельного ТВ. Я сумею подсоединить к нему другие устройства с помощью стандартных кабелей и разъемов, таких как HDMI, USB, S-video и так далее. Но каждому телевизору нужен собственный пульт дистанционного управления, потому что они не стандартизированы, а так называемые «универсальные» пульты работают далеко не всегда.
Иногда существуют даже конкурирующие стандарты, что кажется контрпродуктивным. (Как однажды сказал специалист по информатике Энди Таненбаум, «самое приятное в стандартах то, что можно выбрать тот, какой больше нравится».) В качестве исторических примеров можно привести «Betamax против VHS» для видеокассет, «HD-DVD против Blu-ray» для видеодисков высокой четкости. В обоих случаях один из них в конечном счете взял верх, но есть примеры, когда сосуществовало несколько стандартов: например, две несовместимые технологии сотовых телефонов, использовавшиеся в США примерно до 2020 года.
В программном обеспечении также есть множество стандартов – например, наборы символов ASCII и Unicode, языки программирования C и C++, алгоритмы шифрования и сжатия, а также протоколы для обмена информацией по сетям.
Стандарты имеют решающее значение для взаимозаменяемости и создания открытого конкурентного ландшафта. Они обеспечивают взаимодействие между независимо созданными проектами и формируют основы для конкуренции в какой-либо области между многими поставщиками, тогда как авторские или запатентованные системы, как правило, ограничивают всех. Естественно, их владельцы предпочитают обязательные деловые отношения[40]. У стандартов также есть недостатки: они могут препятствовать прогрессу, если они некачественные или устаревшие, но все вынуждены пользоваться ими. Но этот недостаток малозаметен на фоне преимуществ.
5.6. Программное обеспечение с открытым исходным кодом
Код, который пишет программист, будь то на ассемблерном языке или (что гораздо более вероятно) на языке высокого уровня, называется исходным кодом (ИК). Результат его компиляции в форму, пригодную для обработки процессором, называется объектным кодом. Это различие, как и несколько других, ранее приведенных мною, может показаться слишком скрупулезным, но оно важно. Программисты способны воспринимать ИК, пусть и, пожалуй, с некоторыми усилиями. Поэтому его можно изучать и адаптировать, а любые содержащиеся в нем инновации или идеи будут видны. Однако объектный код претерпевает столько изменений, что обычно по нему невозможно восстановить что-то, даже отдаленно похожее на исходный код. Также из него нельзя извлечь конструкции, которые пригодились бы, чтобы создать другие версии или хотя бы понять, как работает продукт. Вот почему большинство коммерческих программ распространяется только в виде объектного кода. ИК – это ценный секрет, так что его хранят под замком (метафорически, а может, и в буквальном смысле).
Концепция открытого исходного кода (open source) описывает альтернативную ситуацию, при которой ИК свободно доступен для изучения и улучшения.
В прежние времена большинство ПО разрабатывалось компаниями, и основная часть исходного кода держалась в секрете как коммерческая тайна разработчика. Ричард Столлман, программист из Массачусетского технологического института (МТИ), досадовал из-за того, что не мог исправить или улучшить программы, которые он использовал, поскольку их ИК защищали права собственности, что перекрывало доступ к нему. В 1983 году Столлман запустил проект, названный им GNU[41] (https://www.gnu.org/), для создания бесплатных и открытых версий важных программных комплексов – например, операционной системы и компиляторов для ЯП. Он также основал некоммерческую организацию Free Software Foundation (Фонд свободного программного обеспечения) для поддержки открытого исходного кода. Ричард стремился создавать перманентно «свободное» ПО в том смысле, что на него не брали бы патенты и не обременяли бы ограничениями владения. Своей цели он достиг, распространяя разработки под отлично продуманной лицензией авторского права «Универсальная общественная лицензия GNU» (GNU General Public License, или GPL).
В преамбуле к GPL говорится:
Лицензии для большинства программных продуктов и других практических разработок предназначены для того, чтобы лишить вас свободы делиться работами и изменять их. В отличие от них, универсальная общественная лицензия GNU предоставляет вам право свободно делиться всеми версиями программы и изменять их, чтобы она гарантированно оставалась свободным программным обеспечением для всех своих пользователей.
В GPL уточняется, что ПО с такой лицензией можно использовать бесплатно, но если оно предоставляется кому-то еще, то распространитель должен предоставить для исходного кода аналогичную лицензию «бесплатного использования для любых целей». GPL достаточно строга: уже случалось, что суд обязывал компании, нарушившие ее условия, либо прекратить использовать код, либо распространить их ИК, созданный на основе лицензионного.
Благодаря проекту GNU, поддерживаемому компаниями, организациями и частными лицами, удалось создать обширную коллекцию средств разработки программ и приложений, все элементы которой защищены GPL. Другие приложения и документы свободного распространения имеют схожие лицензии – например, Creative Commons, которая сопровождает многие изображения в «Википедии». В некоторых случаях версии с открытым ИК устанавливают стандарт, по которому оцениваются запатентованные коммерческие версии. Открытый исходный код имеют браузеры Firefox и Chrome, а также Apache и NGINX, два наиболее распространенных вебсервера, и ОС Android для мобильных телефонов.
Языки программирования и вспомогательные инструменты в настоящее время почти всегда имеют открытый ИК. Будь они строго проприетарными, при создании новых ЯП возникли бы затруднения. За последнее десятилетие Google разработала и выпустила Go, Apple – Swift, Mozilla – Rust, a Microsoft предоставила доступ к C# и F#, которые долгие годы защищались правами собственности.
Пожалуй, наиболее заметный проект с открытым исходным кодом – операционная система Linux. Она широко используется частными лицами и крупными коммерческими предприятиями, такими как Google, где вся инфраструктура управляется на этой ОС. Вы можете бесплатно загрузить исходный код Linux с kernel.org, использовать его в личных целях и вносить любые изменения. Но если вы распространяете его в любой форме (например, в новом гаджете с какой-либо операционной системой), вы обязаны сделать ваш исходный код доступным под той же GPL. Оба моих автомобиля от разных производителей работают на Linux, и глубоко внутри системы экранного меню каждого из них находятся уведомление о GPL и ссылка. Перейдя по ней, я смог загрузить код из интернета (не из машины!) – почти гигабайт ИК Linux58.
Открытый исходный код интригует. Как можно заработать деньги, раздавая ПО? Почему программисты добровольно вносят вклад в проекты с открытым ИК? Может ли открытый исходный код, написанный энтузиастами, оказаться качественнее проприетарного ПО, разработанного большими командами скоординированных профессионалов? Представляет ли доступность ИК угрозу национальной безопасности?
Некоторые из этих вопросов, по-прежнему интересующих экономистов и социологов, начинают проясняться. Например, Red Hat[42], основанная в 1993 году, к 1999 году стала публичной компанией, акции которой торговались на Нью-Йоркской фондовой бирже, а в 2019-м ее приобрела IBM за 34 миллиарда долларов. Red Hat распространяет исходный код Linux, который можно бесплатно скачать в интернете, – но зарабатывает деньги, получая оплату за поддержку, обучение, обеспечение качества, интеграцию и другие услуги. Многие программисты открытого ИК официально трудоустроены в компаниях, которые используют этот код и вносят в него свой вклад. IBM, Facebook[43] и Google – яркие примеры такого подхода, но, конечно, не единственные. Microsoft в настоящее время – один из крупнейших разработчиков ПО с открытым исходным кодом. Компании выигрывают от того, что направляют развитие программ в нужную им сторону, и от того, что другие люди исправляют их ошибки и вносят усовершенствования.
Не все программное обеспечение с открытым ИК лучшее в своем роде, и порой новые версии таких средств выходят заметно позже, чем у коммерческих систем, по которым они смоделированы. Но что касается основных программных инструментов и систем, то здесь продукты с открытым исходным кодом трудно превзойти.
5.7. Краткие выводы
Языки программирования – это средство, которое помогает нам сообщить компьютерам, что делать. Между естественными и искусственными языками, созданными для облегчения процесса написания кода, имеются сходства (хотя с этой идеей не стоит перегибать палку). Одна очевидная параллель заключается в том, что существуют тысячи ЯП, но часто используются, возможно, только несколько сотен, а подавляющее большинство программ, работающих в настоящее время, приходятся всего на два десятка из них. Некоторые программисты считают – и часто отстаивают свое мнение, – что какие-то языки лучше других. Но одна из причин такого разнообразия состоит в том, что ни один язык не идеален для решения всех задач разработчиков. Всегда есть ощущение, что какой-нибудь подходящий новый язык сделает программирование намного проще и продуктивнее. Также ЯП развиваются, чтобы воспользоваться преимуществами постоянно улучшающихся аппаратных ресурсов. Когда-то программистам приходилось напряженно трудиться, чтобы уместить программу в имеющуюся память. Сегодня это не такая уж проблема. Языки предоставляют механизмы, которые автоматически управляют использованием памяти, так что программистам не приходится много думать об этом.
Вопросы интеллектуальной собственности для ПО решаются сложно, особенно в отношении патентов, где серьезную негативную роль играют тролли. С авторским правом ситуация проще, но все еще остаются нерешенными основные юридические вопросы – например, статус API. Как это часто бывает, на появление новых технологий закон реагирует не слишком быстро (пожалуй, он и не может иначе) – а когда вопрос разрешается, то в каждой стране это происходит по-своему.