Компьютерные программы как системы

Программа::система («системная» вроде ОС или СУБД,программное приложение/application) в момент ихработы --- это вещь, физический объект, она занимает место в пространстве-времени, она материальна. По работающей программе (части компьютера, которая меняет своё состояние в ходе работы программы) можно «постучать», можно ткнуть в неё пальцем. Работающая программа --- физическая часть компьютера, которая проводит вычисления этой программы в ходе её работы. Программа описывается её алгоритмом (описание порядка вычислений), документированном в виде программного кода на каком-то языке (машинном в памяти компьютера в момент исполнения программы, языке программирования в момент написания программы). Но алгоритм не меняет своего состояния, когда он «работает», ибо это описание. А вот компьютер, интерпретирующий алгоритм, меняет своё состояние в ходе исполнения этого алгоритма --- и та его часть, которая занята выполнением алгоритма, и есть программа::система. Dijkstra определяет программу так: «программа = алгоритм + структуры данных». Структурированные данные меняют своё состояние в ходе выполнения программы. Алгоритм тоже может меняться в ходе выполнения программы (часть алгоритма может вычислить другую часть алгоритма как свои данные, а потом эта другая часть алгоритма тоже будет вычислена). В любом случае, программа (алгоритм и данные в памяти, «оживляемые» вычислителем с каким-то процессором --- если речь идёт о классической компьютерной архитектуре, но тут могут быть варианты с разделением процессора и памяти, не всегда они чётко разделимы) в ходе её работы --- это объект, который меняет свои состояния и тем самым как-то себя ведёт, это объект четырёхмерного пространства-времени, его можно считать системой.

У программы как физического объекта в момент работы есть разные состояния, которые представляют собой физические состояния оперативной памяти и регистров процессора, отражающие текущее состояние алгоритма и изменяющихся в ходе выполнения программы данных. Компьютер занят физическими процессами/изменениями/взаимодействиями своих составных частей (памяти, процессора) в ходе вычисления данных по алгоритму как описанию программы. Эти процессы в компьютере занимают какое-то место в физическом мире: пространство, в котором расположены взаимодействующие части компьютера, и время, во время которого программа (то есть части компьютера в её составе) проводит вычисления:

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

Поэтому программисты, которые считают, что их инженерная работа закончена в момент написания исходного кода (алгоритма и структур данных) --- эти программисты глубоко неправы, и это типичная ошибка. Из признания этой ошибки появилось целые движения DevOps[1], SRE[2] и platform engineering[3] --- программисты признали, что они должны быть озабочены не только разработкой кода программы как алгоритма и структур данных (время Development), но и быть операторами работающей на серверах программы (время Operations). Подробнее об этом будет в курсе «Системная инженерия».

Исходный код --- это описание программы (оно делается «в классах», как любое проектирование, один исходный код описывает множество возможных экземпляров программы). Перед использованием/эксплуатацией программы её нужно изготовить, используя исходный код в качестве «описания с детальностью, достаточной для изготовления»: откомпилировать, собрать, разместить в оперативной памяти нужного компьютера (возможно, перед этим оформив в какой-то контейнер) и передать на неё управление. Сейчас и исходный код на языке становится промежуточным внутренним представлением, как и машинный код: описание программы делается на естественном языке, а потом текст программы пишет и отдаёт его на выполнение AI-агент, чаще всего называемый Copilot. Нам не так важны сейчас эти особенности создания программы из каких-то описаний. Нам важней сам факт различения

  • исходного кода::описания программы (алгоритма и структур данных),
  • Файла::документ с описанием программы («информационный объект»),
  • работающей программы::система.

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

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

Итак, первая ошибка --- это считать, что исходный код программы и есть программа. Нет! Исходный код --- описание программы!

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

Как только разработчик забывает, что его программа только цифровой двойник физического двойника, и что он разрабатывает целую систему из цифрового двойника и физического двойника, то проект будет провален. Это как если бы разработчика самолёта не интересовал самолёт в целом, но только его система управления. Нет, работоспособность ERP-системы проверяется не по тому, что она сама работает, а по тому, как она управляет потоком комплектующих через предприятие, важно поведение физического двойника. Работоспособность системы управления самолётом (цифровой двойник) определяется не сама по себе, но по работоспособности самолёта (физический двойник). Важно поведение физического двойника, и создание и развитие системы из подсистем цифрового и физического двойника. И даже в боле слабых вариантах цифровой тени и цифровой модели это тоже выполняется: цифровой двойник --- это автоматическая система управления, цифровая тень --- автоматизированная система управления --- она получает данные с датчиков физического двойника, но вот дальше просто показывает состояние оператору, и тот «руками» управляет системой. Цифровая модель и данные получает «вручную», и выдаёт параметры управления для «ручного» управления. Скажем, навигатор в автомобиле с автопилотом --- цифровой двойник, навигатор со спутниковым датчиком и голосом говорящий, куда рулить --- цифровая тень, а если навигатору надо говорить, где вы находитесь и он будет говорить, куда дальше ехать --- а рулить надо вам, то это цифровая модель. Разговор о софте необходимо сегодня вести с учётом того, что этот софт или сам по себе агент, или цифровой двойник, или цифровая тень, или цифровая модель какой-то другой системы --- физического двойника (ну, или софт --- часть сети софтов, digital twin network, и всё равно нужно думать о физическом двойнике, который управляется этой сетью софтов).

Третья ошибка «программы как системы, которую мы разрабатываем» ---считать саму программу разрабатываемой системой, а не какой-то подсистемой в ситуации, когда речь идёт о поддержке работы какого-то оргзвена с людьми или другими программами. Если ваша программа выдачи зарплаты чудесно работает, а вы забыли научить работников предприятия или AI-агентов (или даже другие программы в рамках автоматизации) мастерству работы с этой программой, то это означает, что программа не работает: зарплата-то не выдаётся! Если оплата ведётся только «за программу», то можно потом долго спорить, кто оплачивает обучение сотрудников или AI-агентов, или оплачивает перепрограммирование всех связанных программ (которое может быть много дороже разработки самой обсуждаемой программы выдачи зарплаты).

В случае ошибки с цифровым двойником программа и её данные (например, в базах данных) понимается как книга: нас чаще всего волнует не книга, и даже не содержание книги (сам книжный текст), а описанное текстом книги. Волнуют описанные в книге миры, не волнует сама красота слога (ну, разве что «особых ценителей», если речь идёт о беллетристике, письме ради письма). С программами то же самое: волнует описанный программой фрагмент окружающего мира, не волнует сама программа (волнует не больше, чем книга и её текст). Если у вас программа заказа химчистки, то вас волнует одежда, которая из грязной станет чистой, деньги, которые с вас спишут, варианты транспортировки вещей на чистку и назад. Программа волнует программиста, но это его личное дело: всех остальных волнует, как эта программа справится с собственно ситуацией химчистки, да и программиста тоже должно это волновать. Если программа будет шикарна как софт, но химчистка с ней будет невозможна --- то это провал проекта! Софт коварен, если его представлять целевой системой, он чаще всего работает с описаниями целевых систем, ищите за ними физический мир, изменения в котором имеют значение!

В случае ошибки с «в нашей ответственности только программа, про людей и организацию ничего не знаем» происходит включение в целевую (или даже «нашу») систему только софта, но не оргзвена той службы/service provider, которая выполняет какой-то осмысленный сервис (работы, меняющие что-то вовне этой службы), поддерживаемого при помощи софта. В корпоративной разработке софта клиенты ожидают по итогам этой разработки не столько корректную работу компьютера с приложением::программа, сколько корректную работу той части организации, которую должен этот компьютер поддержать: люди в организации должны вместе с программой сработать по какому-то организационному алгоритму, «рабочему процессу», выполнить регламент работы, в котором учтено использование программы --- то есть в них должно сработать «мастерство» ---аналог программы: . Софт без людей не работает, нужно предъявлять работу софта с людьми (хотя сейчас всё чаще и чаще люди заменяются другим софтом, в том числе софтом с AI), но на контроле всё-таки не изменения состояний самой программы (поведение программы), а изменение состояний чего-то вовне программы.

Такой совместный сервис людей и компьютеров называется обычно workflow («поток работ»::поведение), но не менее часто называют такой поток работ и «оргпроцесс», и «рабочий процесс», обращая внимание не на собственно экземпляры работы, а на метод/способ работы и заведомо включая операции программы в общие операции оргпроцесса. Программа-в-разработке --- это чаще всего только часть целевого оргпроцесса-в-разработке. И сдавать готовую программу-в-работе не удастся саму по себе, её работа никому не нужна. Нужен оргпроцесс-в-работе, в котором программа только часть, а ещё там участвуют и другие программы, и люди, и иногда ещё и какое-то другое оборудование, и все они должны правильно взаимоизменяться. Если «приложение учёта деловых поездок» работает, но в командировку никто отправиться не может, потому как никто не знает, как пользоваться программой, то за работу программы платить не будут. Платить будут за деловые поездки, которые получатся только тогда, когда сработают вместе люди и «приложение учёта деловых поездок». За работоспособность только софта платить никто не будет! Софт обычно только часть той системы, которая создаётся и развивается проектом разработки!

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

Дело ещё осложняется тем, что программы/софт/приложения как информационные объекты (носители информации с описаниями алгоритмов и данными в базах данных) могут выстраиваться в длинные цепочки, в которых на базе одних описаний программы (или даже программы-внутри-подразделений-с-людьми) меняют другие описания, дальше третьи --- и только в конце такой длинной цепочки «описанных описаний» наступает желаемое изменение внешнего физического мира. Вы можете долго описывать себя, где взять ваши деньги, ваши намерения, записывать промежуточные результаты принятия решений всевозможными другими программами и людьми в компьютер и/или телефон, но только в конце длинной цепочки заказа авиабилетов и проверки билетов, а также проверки наличия самолёта на стоянке, вас физически пропустят на самолёт: сотни самых разных программ и людей, часть из которых вам известна, а часть даже неизвестна, срабатывают, чтобы получить в физическом мире ваше состояние «я сел в самолёт, который готов к перелёту». Если этого результата не случится, все эти программы и люди сработали зря. Поэтому не ленитесь раскручивать все эти «описания описаний»,все длинные цепочки программ и программ-с-людьми --- пока не дойдёте до изменений в физическом мире, ради которых всё делается. И убедитесь, что в физическом мире будет всё в порядке. Да, это трудно, но системное мышление заставляет развернуть ваше внимание в эту сторону. Это важно: выход обсуждений работы софта в обсуждение изменений физического мира --- это имеет значение, если надёжных и ожидаемых изменений в физическом мире не будет, то проект разработки софта можно и не делать вообще, всё одно ведь в физическом мире ничего не изменится!

Например, ровно эта связь между объектами софта и физического мира обсуждается в подходе DDD (domain-driven design), где требуется, чтобы объекты программы напрямую отражали объекты физического мира в предметной области работы компании (впрочем, там требуют и отражения объектов ментального мира --- мира описаний, но это для нас не так важно сейчас)[5].


  1. https://en.wikipedia.org/wiki/DevOps, есть похожий вариант этого движения, SRE и более современный вариант platform engineering ↩︎

  2. https://en.wikipedia.org/wiki/Site_Reliability_Engineering ↩︎

  3. https://platformengineering.org/blog/what-is-platform-engineering, https://thenewstack.io/how-is-platform-engineering-different-from-devops-and-sre/ ↩︎

  4. Тут есть нюанс, связанный с фон-неймановской архитектурой: программа может быть рассмотрена и как данные на носителе, и как исполняемый объект. То же относится к «программе в мозге»: лежит ли в мозге в его нейронах только описание, или же нейронная сеть сейчас именно «вычисляет» что-то, то есть там работает программа-как-процесс в физическом мире --- это тот самый вопрос про «материальность мысли». Это нюансы, мы их тут не рассматриваем. Совет тут --- рассмотреть вероятность, с какой речь идёт о «просто документированном описании программы/мысли» или «исполняющейся программе/думающейся мысли», а вероятность эту брать исходя не из «истинности», а из целей какого-то действия. И обсуждать нужно наиболее вероятную ситуацию, полезную для осуществления вами задуманного. Но это нюанс, он непринципиален в большинстве ситуаций. Мы помним, что у нас тут в системном мышлении не математика со 100% формальными утверждениями, плохо приложимыми к жизни, а вероятностные рассуждения, «как в жизни». ↩︎

  5. https://en.wikipedia.org/wiki/Domain-driven_design ↩︎