Грязная тайна "No-code" сервисов
Не то чтобы совсем никто не был в курсе, просто одни не акцентируют ненужное внимание, чтобы «не загружать техническими деталями» дорогих клиентов, другие верят на слово и про эти детали слушать не хотят. Ну, а я снова пишу статью по мотивам многократных попыток объяснить — что не все так просто и любви за 50 баксов не бывает.
О чем это все
Позволю себе процитировать:
No-code-инструменты позволяют обычным пользователям ПК создавать веб-сайты и приложения без необходимости написания программного кода
C пандемийного 2020го как грибы после дождя полезли «непонятные люди желающие странного»:
выкинуть на мороз дорогого и капризного Васю-программиста, заменив его «no-code» сервисом за 50 баксов в месяц.
Типа такого.
Поветрие было массовым и вообщем-то до сих пор не прошло окончательно.
Хотя опытные пользователи, пережившие 90е и видевшие предыдущие модные поветрия могут заподозрить, что идея 21 века «разработки без программистов», скажем прямо — не нова.
Всю мать ее историю ИТ люди пытались избавиться от дорогих программистов и дать возможность делать кастомизацию софта кривыми руками пользователей.
Знаете как появилось разделение на системных и прикладных программистов? Это была самая первая попытка такого избавления — один из тысячи программистов может написать ОС и компилятор, остальные 999 с этой ОС и компилятором работают.
Но конечно очень быстро этого оказалось мало, поэтому следующим уровнем деградации упрощения стал DSL:
Предметно-ориентированный язык (англ. domain-specific language, DSL — «язык, специфический для предметной области») — компьютерный язык, специализированный для конкретной области применения (в противоположность языку общего назначения, применимому к широкому спектру областей и не учитывающему особенности конкретных сфер знаний). Построение такого языка и/или его структура данных отражают специфику решаемых с его помощью задач[1]. Является ключевым понятием языково-ориентированного программирования.
Хороший пример, с которым большинство читателей точно встречалось — макросы в Microsoft Excel:
Sub Macro1() ' ' Macro1 Macro ' ' Range("B1").Select ActiveCell.FormulaR1C1 = "Hello World" Range("B2").Select End Sub
Примерно такие DSL и используются в Low-Code платформах, чтобы уж совсем одним тасканием кубиков все задачи не решать.
Хотя выглядит все конечно очень красиво и дюже наглядно:
Грязная тайна
Ну да-да — не то чтобы грязная и не совсем тайна, скорее особенность работы этого цирка с конями.
Постараюсь максимально лаконично и доходчиво объяснить в чем тут прикол, но често не обещаю что поймете. Конкретно про no-code/low-code мне почему-то доходчиво не получается доносить мысли.
Для начала давайте посмотрим вот на эту замечательную картинку:
Вообщем возьму быка за рога — то что нарисовано выше в виде схемы это на самом деле программа, самая настоящая.
Все эти квадратики тут — отдельные функции, с параметрами, которые вызываются друг за другом по цепочкам, которые рисуются линиями.
Видите «When»? Это ни что иное как условие вызова — если проверка на условие проходит — выполнение цепочки идет дальше по ветви логики.
Такой себе упрощенный язык программирования, где вместо написания команд вы как дебил таскаете и связываете кубики.
Теперь попрошу вас включить воображение и подумать над тем, как и где схема с картинки выше может храниться.
Пять минут на размышление, а я выдержу МХАТовскую паузу и схожу за кофе.
Конечно оно все хранится в базе, на чужом сервере, к которому вы не имеете прямого доступа. Скорее всего хранится в виде какого-то сложного XML или JSON-документа.
Все это достаточно очевидно, настолько что любой разработчик уже начнет зевать на этом месте.
Но сейчас я напишу лишь одну фразу и у всех причастных немедленно подгорит снизу и начнет дергаться глаз:
Да да мои хорошие, самое время выпить валерианки и пожаловаться на меня своему психологу — что де ваши «личные границы» опять грубо нарушили и расшатали.
Для тех кто не знает и не представляет что это такое, ниже более развернутый рассказ с примерами.
Я использовал для наглядности BPMN-нотацию, которая максимально близка или даже аналогична тому что используется внутри закрытых No-code/low-code платформ.
Больше и детальнее про технологию BPM и BPMN-нотацию в частности я уже рассказал тут, поэтому не буду повторяться.
Вот такая простейшая графическая схема:
в виде XML-документа выглядит как-то так:
<?xml version="1.0" encoding="UTF-8"?> <bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:zeebe="http://camunda.org/schema/zeebe/1.0" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="0.1.0"> <bpmn:process id="Process_1" isExecutable="true"> <bpmn:startEvent id="StartEvent_1" name="Order Placed"> <bpmn:outgoing>SequenceFlow_1bq1azi</bpmn:outgoing> </bpmn:startEvent> <bpmn:sequenceFlow id="SequenceFlow_1bq1azi" sourceRef="StartEvent_1" targetRef="Task_1f47b9v" /> <bpmn:sequenceFlow id="SequenceFlow_09hqjpg" sourceRef="Task_1f47b9v" targetRef="Task_1109y9g" /> <bpmn:sequenceFlow id="SequenceFlow_1ea1mpb" sourceRef="Task_1109y9g" targetRef="Task_00moy91" /> <bpmn:endEvent id="EndEvent_0a27csw" name="Order Delivered"> <bpmn:incoming>SequenceFlow_0ojoaqz</bpmn:incoming> </bpmn:endEvent> <bpmn:sequenceFlow id="SequenceFlow_0ojoaqz" sourceRef="Task_00moy91" targetRef="EndEvent_0a27csw" /> <bpmn:serviceTask id="Task_1f47b9v" name="Collect Money"> <bpmn:extensionElements> <zeebe:taskDefinition type="collect-money" retries="3" /> </bpmn:extensionElements> <bpmn:incoming>SequenceFlow_1bq1azi</bpmn:incoming> <bpmn:outgoing>SequenceFlow_09hqjpg</bpmn:outgoing> </bpmn:serviceTask> <bpmn:serviceTask id="Task_1109y9g" name="Fetch Items"> <bpmn:extensionElements> <zeebe:taskDefinition type="fetch-items" retries="3" /> </bpmn:extensionElements> <bpmn:incoming>SequenceFlow_09hqjpg</bpmn:incoming> <bpmn:outgoing>SequenceFlow_1ea1mpb</bpmn:outgoing> </bpmn:serviceTask> <bpmn:serviceTask id="Task_00moy91" name="Ship Parcel"> <bpmn:extensionElements> <zeebe:taskDefinition type="ship-parcel" retries="3" /> </bpmn:extensionElements> <bpmn:incoming>SequenceFlow_1ea1mpb</bpmn:incoming> <bpmn:outgoing>SequenceFlow_0ojoaqz</bpmn:outgoing> </bpmn:serviceTask> </bpmn:process> <bpmndi:BPMNDiagram id="BPMNDiagram_1"> <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1"> <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1"> <dc:Bounds x="191" y="102" width="36" height="36" /> <bpmndi:BPMNLabel> <dc:Bounds x="175" y="138" width="68" height="12" /> </bpmndi:BPMNLabel> </bpmndi:BPMNShape> <bpmndi:BPMNEdge id="SequenceFlow_1bq1azi_di" bpmnElement="SequenceFlow_1bq1azi"> <di:waypoint xsi:type="dc:Point" x="227" y="120" /> <di:waypoint xsi:type="dc:Point" x="280" y="120" /> <bpmndi:BPMNLabel> <dc:Bounds x="253.5" y="99" width="0" height="12" /> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="SequenceFlow_09hqjpg_di" bpmnElement="SequenceFlow_09hqjpg"> <di:waypoint xsi:type="dc:Point" x="380" y="120" /> <di:waypoint xsi:type="dc:Point" x="440" y="120" /> <bpmndi:BPMNLabel> <dc:Bounds x="410" y="99" width="0" height="12" /> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge id="SequenceFlow_1ea1mpb_di" bpmnElement="SequenceFlow_1ea1mpb"> <di:waypoint xsi:type="dc:Point" x="540" y="120" /> <di:waypoint xsi:type="dc:Point" x="596" y="120" /> <bpmndi:BPMNLabel> <dc:Bounds x="568" y="99" width="0" height="12" /> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNShape id="EndEvent_0a27csw_di" bpmnElement="EndEvent_0a27csw"> <dc:Bounds x="756" y="102" width="36" height="36" /> <bpmndi:BPMNLabel> <dc:Bounds x="734" y="142" width="81" height="12" /> </bpmndi:BPMNLabel> </bpmndi:BPMNShape> <bpmndi:BPMNEdge id="SequenceFlow_0ojoaqz_di" bpmnElement="SequenceFlow_0ojoaqz"> <di:waypoint xsi:type="dc:Point" x="696" y="120" /> <di:waypoint xsi:type="dc:Point" x="756" y="120" /> <bpmndi:BPMNLabel> <dc:Bounds x="726" y="99" width="0" height="12" /> </bpmndi:BPMNLabel> </bpmndi:BPMNEdge> <bpmndi:BPMNShape id="ServiceTask_0lao700_di" bpmnElement="Task_1f47b9v"> <dc:Bounds x="280" y="80" width="100" height="80" /> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="ServiceTask_0eetpqx_di" bpmnElement="Task_1109y9g"> <dc:Bounds x="440" y="80" width="100" height="80" /> </bpmndi:BPMNShape> <bpmndi:BPMNShape id="ServiceTask_09won99_di" bpmnElement="Task_00moy91"> <dc:Bounds x="596" y="80" width="100" height="80" /> </bpmndi:BPMNShape> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram> </bpmn:definitions>
Пример взят из документации Camunda, если вдруг кому интересно. Там еще много такого.
Теперь представьте как это мигрировать.
А пардон, я же тут пишу для обычных людей, не для айтишников. Так что давайте деградировать вместе опустимся до кубиков:
Вот этим жирным красным крестом я пометил блок, которого больше нет, по независящим от вас и «no-code» платформы причинам.
Не договорились, контракт кончился, санкции ввели — что угодно. Причины тут не так важны.
В вашей замечательной чистой, белой и пушистой «no-code» схеме появилась дыра. По независящим от вас причинам. Без вашей воли и без ваших действий.
Как минимум, ваша схема станет работать неправильно, как максимум — сломается совсем и вся автоматизация встанет.
Как супер-максимум — сломается на уровне данных, так что чинить будете с тех.поддержкой и подключением разрабочиков платформы.
Вот про последнее и поговорим.
Работает — не трож!
Как чаще всего делается внутренняя автоматизация бизнеса на «no-code» решениях?
Вы долго и упорно ваяете схему в онлайн-кострукторе, зарабатывая мозоль длинными темными ночами от таскания мышкой всех этих разноцветных блоков. Затем запускаете в работу и забываете.
Проходит полгода-год до момента следующей мелкой правки. И внезапно оказывается что половина созданного уже не работает.
сама «no-code» платформа это тоже софт, у которого есть релизы и обновления, который постоянно дорабатывают и исправляют
Естественно что за эти полгода, пока вы думали что все успешно сделали, запустили и забыли — платформа продолжала обновляться и перезапускаться.
И по идее с вашими данными все должно было быть хорошо и ваши схемы — те самые наборы кубиков со стрелочками также каждый раз должны были обновляться.
миграция пользовательских данных — самый страшный, кромешный п#здец из возможных в ИТ
Даже в крупных ИТ-компаниях этот процесс — русская рулетка, а утеря и порча пользовательских данных из-за кривого обновления были и у Google и у Facebook и у Яндекса с VK.
Причем понять что что-то пошло не так сразу нельзя, а откатывать апдейт по данным запредельно сложно и очень долго.
Вообщем если вы когда-либо видели упавший фейсбук, утерю почтовых сообщений или переписки ВК — знайте что это все из-за обновлений.
Теперь вернемся к нашим баранам — «no-code» решению.
Программа, которая считала себя блок-схемой
набор разноцветных связанных линиями кубиков на красивой блок-схеме — по факту является программой
Со всеми вытекающими последствиями, главные из которых — контроль выполнения и наличие состояния.
Да да, у вашей красивой блок-схемы есть начало и конец (даже несколько) — есть точка запуска, триггер, который запускает всю связанную логику и точки завершения, по которым выполнение прекращается.
Которое надо где-то сохранить и уметь продолжить выполнение вашей блок-схемы с этой конкретной точки.
Именно так оно и есть для обычных программ, которые вы запускаете локально. А теперь представьте что состояние вашей «схемы-программы» хранится в базе данных в виде все той-же XML, с привязкой к структурам данных платформы.
И криворукие разработчики платформы уже выкатывают потенциально опасный апдейт, который все это сломает.
Итого
Идеи из серии «выкинь Васю-программиста на мороз, чтоб не платить столько денег» в ИТ были с самого его начала, где-то с 70х годов прошлого века, если не раньше.
Собственно абсолютно любой массовый бизнес про удешевление для конечного потребителя, кому это удается тот и в дамках.
Тем не менее, мой личный опыт показывает, что все попытки выкинуть именно разработчиков приводят к тому что их «электронная замена» со временем начинает стоить дороже чем содержание живого сотрудника, при этом без гибкости и универсальности homo sapiens.
С технической точки зрения, выбирая «no-code» решение, вы все равно будете писать программу, пусть и в виде блок-схемы.
Шутка юмора зашла уже настолько далеко, что появился термин no-code разработчик, так что это не мои выдумки или троллинг.
А отказ от традиционных средств разработки и своеобразная «культура отмены программистов» в среде модных стартаперов приводит ровно к тому же что и в жизни — вы однажды проснетесь обосранным, с пониманием что все про#бали из-за чужого навязанного мнения.