Иногда полезно залезть в область, в которой вообще не собираешься становиться специалистом. Просто чтобы лучше понимать, что происходит рядом с твоей основной работой, и не воспринимать чужую профессию как магию.
Неожиданно для себя решил немного заняться дизайном. У меня есть годовые цели, и мы с ChatGPT посмотрели на календарь: скоро новый год, а к некоторым из них я даже не приступал. Одна из таких целей — дизайн.
Формально дизайном я занимаюсь давно: открытки к 8 Марта, простые сайты и прочее высокое искусство. UX/UI-дизайнером становиться не собираюсь — это отдельная и довольно сложная профессия. Но фронтендеру базовое понимание дизайна полезно: поправить компоновку, цвет, шрифт или готовый макет, не дёргая дизайнера из-за каждой мелочи.
Поэтому нашёл курс по Figma и повторил первый макет. Сама Figma оказалась не такой страшной. Когда-то я работал в CorelDraw и Adobe Illustrator, поэтому логика знакома: группировка, выравнивание, цвет, обводка, размеры. Главная проблема таких программ для меня всегда одна — надо учить горячие клавиши, иначе однотипные действия начинают съедать уйму времени.
Параллельно читаю «Типы в языках программирования» Бенджамина Пирса и дошёл до ООП.
Обычно ООП объясняют через животных: есть класс «Животное», от него наследуются кошка и собака, одна мяукает, другая лает. Все всё поняли, аплодисменты, расходимся.
Но внутри никаких кошечек и собачек, конечно, нет. Пирс показывает объектную модель без привычной магии классов. Оказывается, основные механизмы ООП можно собрать из более простых конструкций языка:
объект → запись функций;
метод → функция;
переменные экземпляра → ссылки (
инкапсуляция → замыкание;
наследование → расширение записи методов;
То есть ООП не требует специальных «объектно-ориентированных» конструкций языка. Классы — во многом удобная оболочка над функциями, записями, ссылками и рекурсией.
После такого разбора на привычное ООП начинаешь смотреть немного иначе.
И ещё немного компьютерной археологии.
Все наверняка слышали про WebAssembly (Wasm) — низкоуровневый формат, позволяющий выполнять в браузере код, скомпилированный, например, из C, C++ или Rust. Благодаря Wasm и другим технологиям браузер давно умеет гораздо больше, чем просто показывать HTML.
Например, прямо в браузере можно запускать старые программы и операционные системы. Нашёл Windows 95:
https://www.pcjs.org/software/pcx86/sys/windows/win95/4.00.950/
Для кого-то это музейный экспонат, а я на этой Windows учился в ВУЗе. Довольно странное ощущение: система, которая когда-то была обычным рабочим инструментом, теперь запускается в браузере как часть компьютерной истории.
Дизайн, устройство ООП и Wasm вроде бы находятся довольно далеко друг от друга. Но смысл для меня один: иногда полезно выходить за пределы своей специализации и разбираться, как устроены соседние области.
Не для того, чтобы срочно становиться дизайнером, теоретиком языков или разработчиком компиляторов. А хотя бы для того, чтобы следующий незнакомый инструмент уже не выглядел магией.
Неожиданно для себя решил немного заняться дизайном. У меня есть годовые цели, и мы с ChatGPT посмотрели на календарь: скоро новый год, а к некоторым из них я даже не приступал. Одна из таких целей — дизайн.
Формально дизайном я занимаюсь давно: открытки к 8 Марта, простые сайты и прочее высокое искусство. UX/UI-дизайнером становиться не собираюсь — это отдельная и довольно сложная профессия. Но фронтендеру базовое понимание дизайна полезно: поправить компоновку, цвет, шрифт или готовый макет, не дёргая дизайнера из-за каждой мелочи.
Поэтому нашёл курс по Figma и повторил первый макет. Сама Figma оказалась не такой страшной. Когда-то я работал в CorelDraw и Adobe Illustrator, поэтому логика знакома: группировка, выравнивание, цвет, обводка, размеры. Главная проблема таких программ для меня всегда одна — надо учить горячие клавиши, иначе однотипные действия начинают съедать уйму времени.
Параллельно читаю «Типы в языках программирования» Бенджамина Пирса и дошёл до ООП.
Обычно ООП объясняют через животных: есть класс «Животное», от него наследуются кошка и собака, одна мяукает, другая лает. Все всё поняли, аплодисменты, расходимся.
Но внутри никаких кошечек и собачек, конечно, нет. Пирс показывает объектную модель без привычной магии классов. Оказывается, основные механизмы ООП можно собрать из более простых конструкций языка:
объект → запись функций;
метод → функция;
переменные экземпляра → ссылки (
ref);инкапсуляция → замыкание;
наследование → расширение записи методов;
self → рекурсивная передача объекта самому себе.То есть ООП не требует специальных «объектно-ориентированных» конструкций языка. Классы — во многом удобная оболочка над функциями, записями, ссылками и рекурсией.
После такого разбора на привычное ООП начинаешь смотреть немного иначе.
И ещё немного компьютерной археологии.
Все наверняка слышали про WebAssembly (Wasm) — низкоуровневый формат, позволяющий выполнять в браузере код, скомпилированный, например, из C, C++ или Rust. Благодаря Wasm и другим технологиям браузер давно умеет гораздо больше, чем просто показывать HTML.
Например, прямо в браузере можно запускать старые программы и операционные системы. Нашёл Windows 95:
https://www.pcjs.org/software/pcx86/sys/windows/win95/4.00.950/
Для кого-то это музейный экспонат, а я на этой Windows учился в ВУЗе. Довольно странное ощущение: система, которая когда-то была обычным рабочим инструментом, теперь запускается в браузере как часть компьютерной истории.
Дизайн, устройство ООП и Wasm вроде бы находятся довольно далеко друг от друга. Но смысл для меня один: иногда полезно выходить за пределы своей специализации и разбираться, как устроены соседние области.
Не для того, чтобы срочно становиться дизайнером, теоретиком языков или разработчиком компиляторов. А хотя бы для того, чтобы следующий незнакомый инструмент уже не выглядел магией.
www.pcjs.org
Microsoft Windows 95: First Retail Release
PCjs offers a variety of online machine emulators written in JavaScript. Run DOS, Windows, OS/2 and other vintage PC applications in a web browser on your desktop computer, iPhone, or iPad. An assortment of microcomputers, minicomputers, terminals, programmable…
👍3
Начал проходить курс «Computer Science во Frontend». Первая тема — массивы. Казалось бы, что там вообще может быть сложного? Но довольно быстро выясняется, что привычный
И вот тут появляются типизированные массивы —
Но если требуется прочитать бинарный файл какого-нибудь неведомого формата, веселье только начинается. Одни данные могут занимать один байт, другие два или четыре, а многобайтовые числа ещё и записываются в разном порядке:
С текстом отдельная история. UTF-8 и UTF-16 используют переменное количество байтов для представления символов, поэтому просто читать файл по одному байту и считать, что каждый байт равен одному символу, не получится. Для декодирования текста есть отдельные инструменты вроде
Получается довольно занятная схема: сначала считываем бинарный файл в область памяти
Если сократить всю тему до одной схемы, получится примерно так:
Все три объекта смотрят на одни и те же 16 байт памяти, но каждый по-своему.
Конечно, совершенно не обязательно каждый бинарный формат разбирать вручную. Есть готовые библиотеки вроде https://github.com/keichi/binary-parser, которые позволяют описать структуру данных и получить готовый объект. Протокол Protobuf https://protobuf.dev/, который используется в том числе вместе с gRPC, тоже работает с компактным бинарным представлением данных. Под капотом всё снова сводится к байтам, длинам полей и правилам их интерпретации.
В общем, я начал тему с мыслью: «Что может быть сложного в массивах?» Оказалось, довольно много, если опуститься на уровень памяти и бинарных данных. Обычный
Array в JavaScript — это достаточно хитрая абстракция, а не просто кусок непрерывной памяти, как массив в C. Обычно этого совершенно достаточно, пока не приходится лезть в бинарные данные.И вот тут появляются типизированные массивы —
TypedArray. Это структуры JavaScript для работы с числами одного типа в непрерывной области памяти. Сама память хранится в ArrayBuffer, а Uint8Array, Int32Array, Float64Array и другие типы позволяют смотреть на неё по-разному. В отличие от обычного Array, размер такого массива фиксирован, а один и тот же ArrayBuffer можно одновременно интерпретировать разными способами.Но если требуется прочитать бинарный файл какого-нибудь неведомого формата, веселье только начинается. Одни данные могут занимать один байт, другие два или четыре, а многобайтовые числа ещё и записываются в разном порядке:
little-endian или big-endian. Для такой работы есть DataView, который позволяет читать из ArrayBuffer значения нужного типа, с нужного смещения и с нужным порядком байтов.С текстом отдельная история. UTF-8 и UTF-16 используют переменное количество байтов для представления символов, поэтому просто читать файл по одному байту и считать, что каждый байт равен одному символу, не получится. Для декодирования текста есть отдельные инструменты вроде
TextDecoder, а TypedArray и DataView остаются уровнем ниже — они работают непосредственно с байтами и числами.Получается довольно занятная схема: сначала считываем бинарный файл в область памяти
ArrayBuffer, а потом накладываем на неё нужные представления. Причём при работе с многобайтовыми типами приходится учитывать смещения и выравнивание данных. В какой-то момент понимаешь, что фраза «просто прочитать несколько байтов из файла» звучит примерно так же оптимистично, как «просто подключить ещё один сервис».Если сократить всю тему до одной схемы, получится примерно так:
const buffer = new ArrayBuffer(16);
const uint8 = new Uint8Array(buffer);
const uint32 = new Uint32Array(buffer);
const dataView = new DataView(buffer);
Все три объекта смотрят на одни и те же 16 байт памяти, но каждый по-своему.
Uint8Array видит последовательность байтов, Uint32Array — 32-битные беззнаковые числа, а DataView позволяет самому выбирать тип, смещение и порядок байтов. Память одна, способов её интерпретировать несколько — и вот тут тема массивов внезапно становится заметно интереснее.Конечно, совершенно не обязательно каждый бинарный формат разбирать вручную. Есть готовые библиотеки вроде https://github.com/keichi/binary-parser, которые позволяют описать структуру данных и получить готовый объект. Протокол Protobuf https://protobuf.dev/, который используется в том числе вместе с gRPC, тоже работает с компактным бинарным представлением данных. Под капотом всё снова сводится к байтам, длинам полей и правилам их интерпретации.
В общем, я начал тему с мыслью: «Что может быть сложного в массивах?» Оказалось, довольно много, если опуститься на уровень памяти и бинарных данных. Обычный
Array позволяет обо всём этом почти никогда не думать, и в этом как раз его удобство. Но стоит заглянуть уровнем ниже — и за привычным [] внезапно обнаруживается целый маленький мир.GitHub
GitHub - keichi/binary-parser: A blazing-fast declarative parser builder for binary data
A blazing-fast declarative parser builder for binary data - keichi/binary-parser
👍3
Чем дальше прохожу «Computer Science во Frontend», тем чаще оказывается, что привычные вещи устроены совсем не так просто, как кажутся сверху. Массив уже оказался не совсем массивом, теперь добрался до RxJS и устройства памяти процесса. В обоих случаях примерно одна история: пока работаешь через удобную абстракцию — всё понятно, а стоит заглянуть внутрь, начинается самое интересное.
Начал проходить миникурс по RxJS https://rxjs-course-avy.web.app/. В React основной подход довольно понятный: изменилось состояние → компонент перерендерился, а асинхронность обычно закрывается через
Многие данные во frontend — это вообще не одно значение, а поток значений во времени: ввод пользователя, HTTP-запросы, события роутера, WebSocket, таймеры.
По собственному опыту особенно хорошо смысл потоков становится понятен, когда появляется два-три зависимых асинхронных запроса, а предыдущие ещё надо уметь отменять. Можно собрать это на
Параллельно в курсе «Computer Science во Frontend» дошёл до сегментов памяти программы. Со школы помню классическую картинку: есть код программы, данные, куча растёт вверх, стек растёт вниз — всё просто и понятно. И это даже не то чтобы неправильно. Просто реальная память процесса, как обычно, решила не ограничиваться школьной схемой и добавить немного подробностей.
Память процесса представляет собой набор областей виртуального адресного пространства. Есть сегмент кода
Ещё выше в классической схеме находится
Ещё со школы помню страшилку: «Не кладите, дети, много данных в стек — переполнится и всё сломается». И здесь тоже всё немного интереснее: в стеке лежат далеко не только восьмибайтовые указатели, а целые стековые кадры функций, локальные значения и служебные данные. Поэтому условные 8 МБ стека выглядят огромными ровно до момента, пока не появляется глубокая рекурсия или большой локальный массив.
В общем, очередная тема из серии «вроде всё знал, пока не начал разбираться». С RxJS оказывается, что асинхронность удобнее воспринимать не как набор отдельных операций, а как поток событий. С памятью — что привычные стек и куча являются только частью гораздо более сложной картины. Похоже, весь Computer Science примерно так и устроен: сначала тебе дают простую модель, а потом постепенно объясняют, сколько всего она от тебя скрывала.
Начал проходить миникурс по RxJS https://rxjs-course-avy.web.app/. В React основной подход довольно понятный: изменилось состояние → компонент перерендерился, а асинхронность обычно закрывается через
Promise, async/await, useEffect, TanStack Query или внешний state-менеджер. В Angular потоки встроены в экосистему гораздо глубже, поэтому здесь приходится немного перестраивать привычный способ мышления.Многие данные во frontend — это вообще не одно значение, а поток значений во времени: ввод пользователя, HTTP-запросы, события роутера, WebSocket, таймеры.
Observable позволяет такие потоки объединять, фильтровать и преобразовывать через map, switchMap, combineLatest, debounceTime и другие операторы. Современный Angular при этом разделяет задачи: Signals удобны для состояния UI, а RxJS остаётся сильным инструментом для сложной асинхронности.По собственному опыту особенно хорошо смысл потоков становится понятен, когда появляется два-три зависимых асинхронных запроса, а предыдущие ещё надо уметь отменять. Можно собрать это на
Promise и async/await, но довольно быстро начинается ручное управление состояниями, отменами и гонками. С потоками такая логика иногда получается заметно чище, хотя первое время на RxJS смотришь примерно как на отдельный язык программирования внутри TypeScript.Параллельно в курсе «Computer Science во Frontend» дошёл до сегментов памяти программы. Со школы помню классическую картинку: есть код программы, данные, куча растёт вверх, стек растёт вниз — всё просто и понятно. И это даже не то чтобы неправильно. Просто реальная память процесса, как обычно, решила не ограничиваться школьной схемой и добавить немного подробностей.
Память процесса представляет собой набор областей виртуального адресного пространства. Есть сегмент кода
text с машинными инструкциями, .data для инициализированных глобальных и статических переменных, .bss для неинициализированных, а дальше располагается heap для динамической памяти. Отдельно существует область mmap, куда могут отображаться файлы, анонимная память и динамические библиотеки вроде libc.so.Ещё выше в классической схеме находится
stack: там живут данные вызовов функций, локальные значения, адреса возврата и служебная информация. Стек обычно растёт в сторону уменьшения адресов, а расположение самих областей может меняться при каждом запуске из-за ASLR (Address Space Layout Randomization — рандомизации размещения адресного пространства). Поэтому память процесса — это не один большой непрерывный кусок RAM, а организованное виртуальное пространство, которое операционная система отображает на реальную память по мере необходимости.Ещё со школы помню страшилку: «Не кладите, дети, много данных в стек — переполнится и всё сломается». И здесь тоже всё немного интереснее: в стеке лежат далеко не только восьмибайтовые указатели, а целые стековые кадры функций, локальные значения и служебные данные. Поэтому условные 8 МБ стека выглядят огромными ровно до момента, пока не появляется глубокая рекурсия или большой локальный массив.
В общем, очередная тема из серии «вроде всё знал, пока не начал разбираться». С RxJS оказывается, что асинхронность удобнее воспринимать не как набор отдельных операций, а как поток событий. С памятью — что привычные стек и куча являются только частью гораздо более сложной картины. Похоже, весь Computer Science примерно так и устроен: сначала тебе дают простую модель, а потом постепенно объясняют, сколько всего она от тебя скрывала.
RxJS Ninja
RxJS Ninja — интерактивный курс RxJS: уроки, практика, справочник операторов
Бесплатный интерактивный курс RxJS на русском: уроки, практика, справочник операторов (of, from, map, switchMap, mergeMap, catchError) и real-world use cases.…
👍4
Продолжаю смотреть курс «Computer Science во Frontend». Дошёл до многопоточности. Ну какая многопоточность в JavaScript? Это не про нас. JavaScript однопоточный. Многопоточность — это где-то там, в больших и мощных Java и Go. Но, как обычно, есть нюансы. Причём сразу два: есть Web Workers, которые предоставляет окружение, и есть асинхронность, которой тоже нужно как-то грамотно управлять.
Посмотрел на пример с Web Worker, где автор создаёт два потока, — без слёз на этот код не посмотреть. Worker выполняет отдельный JS-файл, а если хочется собрать всё динамически, начинаются развлечения: функцию превращаем в строку, оборачиваем в
А вот управление асинхронностью оказалось намного интереснее. Мне понравилась структура со страшным названием «асинхронный семафор (барьер)». Идея очень простая: есть несколько независимых операций, и продолжить работу можно только после завершения всех. Например, при запуске проекта нужно подключиться к базе данных, прочитать YAML-конфиг и поднять Redis. Только когда готовы все три компонента, выполняем callback. (см. рисунок)
В примере есть три флага.
У меня когда-то была аналогичная задача на фронте: выполнить код только после того, как всё загрузилось и инициализировалось. Проект был древний и страшный, а я помню, как извращался с Redux. Решение в итоге собрал из говна и палок. Сейчас смотрю на барьер и думаю: вот же оно. Иногда полезно изучить название конструкции, которую ты когда-то самостоятельно изобрёл в гораздо более страшном виде.
Заодно посмотрел блок по функциональному программированию. Раньше все эти функторы, монады и каррирование выглядели как тайное знание бородатых программистов на Haskell. А потом выясняется, что половиной этих идей мы спокойно пользуемся в обычном JavaScript. Функции высшего порядка принимают и возвращают функции, замыкания сохраняют окружение, каррирование разбивает функцию на цепочку вызовов, а композиция позволяет собирать сложное поведение из простых функций.
Даже привычные
И вот тут я наконец понял основную идею функционального программирования. Значение помещается в некоторый контекст, а функции последовательно преобразуют его, стараясь не вытаскивать наружу и не мутировать состояние вручную. Звучит просто ровно до того момента, пока не узнаешь, что называется страшным словом «монада». Но без десятка нормальных примеров понять это почти невозможно. Но когда начинаешь видеть знакомые
Так что вывод получился забавный. В JavaScript вроде бы нет привычной многопоточности, а функциональное программирование кажется чем-то академическим и далёким. Но начинаешь копать — и выясняется, что асинхронные барьеры, функторы и монады уже давно находятся рядом. Просто раньше я пользовался частью этих идей, не зная, как они называются.
Посмотрел на пример с Web Worker, где автор создаёт два потока, — без слёз на этот код не посмотреть. Worker выполняет отдельный JS-файл, а если хочется собрать всё динамически, начинаются развлечения: функцию превращаем в строку, оборачиваем в
Blob, создаём URL и только потом отдаём Worker. Извращение ещё то. Поэтому если мне действительно понадобится удобная многопоточность, я лучше возьму Go. В JavaScript это инструмент скорее для отдельных специфических задач, чем повседневная модель программирования.А вот управление асинхронностью оказалось намного интереснее. Мне понравилась структура со страшным названием «асинхронный семафор (барьер)». Идея очень простая: есть несколько независимых операций, и продолжить работу можно только после завершения всех. Например, при запуске проекта нужно подключиться к базе данных, прочитать YAML-конфиг и поднять Redis. Только когда готовы все три компонента, выполняем callback. (см. рисунок)
В примере есть три флага.
expectedFlags хранит полный набор условий, а readyFlags — уже выполненные. Каждый участник после завершения работы «поднимает» свой флаг, а барьер проверяет, собрались ли все. Set здесь особенно удобен: повторный barrier.set('database') ничего не сломает и не увеличит счётчик, потому что одинаковые значения в Set не дублируются. Просто и достаточно элегантно.У меня когда-то была аналогичная задача на фронте: выполнить код только после того, как всё загрузилось и инициализировалось. Проект был древний и страшный, а я помню, как извращался с Redux. Решение в итоге собрал из говна и палок. Сейчас смотрю на барьер и думаю: вот же оно. Иногда полезно изучить название конструкции, которую ты когда-то самостоятельно изобрёл в гораздо более страшном виде.
Заодно посмотрел блок по функциональному программированию. Раньше все эти функторы, монады и каррирование выглядели как тайное знание бородатых программистов на Haskell. А потом выясняется, что половиной этих идей мы спокойно пользуемся в обычном JavaScript. Функции высшего порядка принимают и возвращают функции, замыкания сохраняют окружение, каррирование разбивает функцию на цепочку вызовов, а композиция позволяет собирать сложное поведение из простых функций.
Даже привычные
Array и Promise можно рассматривать с этой точки зрения. Array через map ведёт себя как функтор, а через flatMap — как монадическая структура: значение находится в контексте «много значений». Promise — это контекст «значение появится позже», а then позволяет преобразовывать его, не вытаскивая из этого контекста вручную. Maybe/Optional описывает другой контекст — «значение может быть, а может и не быть»; в обычном JS отдалённо похожие задачи решают ?. и ??.И вот тут я наконец понял основную идею функционального программирования. Значение помещается в некоторый контекст, а функции последовательно преобразуют его, стараясь не вытаскивать наружу и не мутировать состояние вручную. Звучит просто ровно до того момента, пока не узнаешь, что называется страшным словом «монада». Но без десятка нормальных примеров понять это почти невозможно. Но когда начинаешь видеть знакомые
Array, map, flatMap и Promise, вся эта страшная теория внезапно становится намного ближе к обычному JavaScript.Так что вывод получился забавный. В JavaScript вроде бы нет привычной многопоточности, а функциональное программирование кажется чем-то академическим и далёким. Но начинаешь копать — и выясняется, что асинхронные барьеры, функторы и монады уже давно находятся рядом. Просто раньше я пользовался частью этих идей, не зная, как они называются.
👍3
Функциональное программирование меня неожиданно увлекло. Особенно рекурсия, которую я считал довольно простой конструкцией: функция вызывает саму себя, и всё. Оказалось, что существует как минимум пять её разновидностей, некоторые весьма замысловатые. А началось всё с бумажной книги, которую я купил давным-давно. Заодно вспомнил интересную методику чтения, позволяющую не тратить десятки часов на книги.
В чистом функциональном программировании нет привычных циклов
Линейная — один рекурсивный вызов, например факториал:
Хвостовая — рекурсивный вызов выполняется последним:
Множественная — несколько вызовов, например Фибоначчи:
Взаимная — функции вызывают друг друга:
Вложенная — результат одного вызова передаётся другому:
Все типы рекурсии я привёл на картинке. Особенно понравился алгоритм Маккарти 91. Выглядит как математическая магия: функция вызывает саму себя внутри собственного вызова, но результат вполне предсказуемый. Казалось бы, знакомая конструкция, а сколько интересного обнаруживается, если копнуть глубже.
Но вернёмся к книге. Она бумажная, а читать техническую литературу от корки до корки — удовольствие сомнительное, особенно когда половина материала уже знакома. Вспомнил методику Андрея Парабеллума из книги «Быстрые результаты. 10-дневная программа повышения личной эффективности». Идея простая: в большинстве книг Non-fiction информация повторяется, а основные мысли зачастую можно уместить в одну статью.
Методика основана на трёх проходах:
Первый (1–3 минуты) — просматриваем оглавление, заголовки и выводы. Решаем, стоит ли читать книгу.
Второй (15 минут) — выборочно читаем интересные главы, ищем ключевые идеи.
Третий (без ограничения времени) — углублённо изучаем самое ценное, делаем заметки и пропускаем второстепенное.
По собственному опыту до третьего прохода доживает ну очень небольшое количество книг. И это нормально. Зачем тратить десять часов, если полезные мысли можно извлечь за двадцать минут? Конечно, сложная техническая литература — исключение, там без практики не обойтись. Ещё полезно оценивать прочитанное по десятибалльной шкале и выносить из книги 1–3 идеи для применения.
В итоге с книгами получается примерно то же самое, что и с программированием: 20% теории и 80% практики. Можно прочитать десять книг по рекурсии, но пока сам не напишешь несколько алгоритмов, толку будет немного. Главное не сколько книг прочитал, а что научился делать после их прочтения.
В чистом функциональном программировании нет привычных циклов
for и while. Вместо них используют рекурсию и функции высшего порядка вроде map, filter и reduce. Вспомнил, что давно купил книгу Мануэля Рубио-Санчеса «Введение в рекурсивное программирование». Решил наконец до неё добраться и выяснил, что существует пять основных типов рекурсии:Линейная — один рекурсивный вызов, например факториал:
n * factorial(n - 1).Хвостовая — рекурсивный вызов выполняется последним:
return factorial(n - 1, n * acc). Допускает оптимизацию стека, но в JavaScript она не гарантирована.Множественная — несколько вызовов, например Фибоначчи:
fibonacci(n - 1) + fibonacci(n - 2).Взаимная — функции вызывают друг друга:
isEven() и isOdd().Вложенная — результат одного вызова передаётся другому:
f(f(n - 1)). Классический пример — алгоритм Маккарти 91, который для чисел ≤ 100 возвращает 91, а для чисел > 100 — число минус 10.Все типы рекурсии я привёл на картинке. Особенно понравился алгоритм Маккарти 91. Выглядит как математическая магия: функция вызывает саму себя внутри собственного вызова, но результат вполне предсказуемый. Казалось бы, знакомая конструкция, а сколько интересного обнаруживается, если копнуть глубже.
Но вернёмся к книге. Она бумажная, а читать техническую литературу от корки до корки — удовольствие сомнительное, особенно когда половина материала уже знакома. Вспомнил методику Андрея Парабеллума из книги «Быстрые результаты. 10-дневная программа повышения личной эффективности». Идея простая: в большинстве книг Non-fiction информация повторяется, а основные мысли зачастую можно уместить в одну статью.
Методика основана на трёх проходах:
Первый (1–3 минуты) — просматриваем оглавление, заголовки и выводы. Решаем, стоит ли читать книгу.
Второй (15 минут) — выборочно читаем интересные главы, ищем ключевые идеи.
Третий (без ограничения времени) — углублённо изучаем самое ценное, делаем заметки и пропускаем второстепенное.
По собственному опыту до третьего прохода доживает ну очень небольшое количество книг. И это нормально. Зачем тратить десять часов, если полезные мысли можно извлечь за двадцать минут? Конечно, сложная техническая литература — исключение, там без практики не обойтись. Ещё полезно оценивать прочитанное по десятибалльной шкале и выносить из книги 1–3 идеи для применения.
В итоге с книгами получается примерно то же самое, что и с программированием: 20% теории и 80% практики. Можно прочитать десять книг по рекурсии, но пока сам не напишешь несколько алгоритмов, толку будет немного. Главное не сколько книг прочитал, а что научился делать после их прочтения.
👍1
Недавно ко мне обратился знакомый программист и рассказал, что есть ненулевой шанс попасть под сокращение. Я вспомнил, что меня самого увольняли раза три-четыре, так что определённый опыт накопился. Сразу оговорюсь: это мой личный опыт, а не универсальная инструкция для всего IT.
Как только в компании начинаются разговоры про оптимизацию, повышение эффективности бизнеса и сложную экономическую ситуацию, я бы задумался. Насколько проект, над которым я работаю, критичен для бизнеса? Некритичный проект могут просто закрыть, а на критичном оставить двух-трёх человек для поддержки. Причём уволить могут довольно быстро, иногда за пару недель.
Первое — финансовая подушка. Я бы начал откладывать деньги, чтобы накопить хотя бы 3–6 месячных окладов. Меня несколько раз увольняли в самые «удачные» периоды: декабрь-январь или летом. В первом случае все на праздниках, во втором — в отпусках. Деньги нужны не только для оплаты коммуналки и питания, но и чтобы не бросаться на первый попавшийся оффер.
Второе — алгоритмы. Причём не видео «Все алгоритмы за два часа», а полноценный курс по решению задач с LeetCode. На собеседовании могут дать две-три задачи по двадцать минут, и это скорее математические головоломки, чем реальное программирование. Я бы не советовал часами биться над каждой задачей самостоятельно. Лучше разобрать существующее решение, воспроизвести его без подсказок и закрепить на похожих задачах. В идеале — 200–300 задач и около полугода подготовки. Рекомендую курс «Подготовка к алгоритмическому собеседованию в BigTech». Самое обидное, что в реальной работе большинство этих трюков практически не используется.
Третье — резюме и собеседования. Сейчас HR активно используют ИИ, поэтому резюме стоит оптимизировать под ключевые слова вакансий, но главное — показать результаты своей работы, а не просто перечислить технологии. Ещё рекомендую посмотреть записи технических собеседований на YouTube и пройти хотя бы одно пробное. На повторение теории вроде асинхронности JS, сборки мусора и работы браузера я бы выделил одну-две недели. Алгоритмы потребуют гораздо больше времени.
Четвёртое — поиск работы. Это отдельная работа, которой я уделял по 4–6 часов в день. Адаптировать резюме, написать сопроводительное письмо, пообщаться с HR, пройти собеседование, подтянуть обнаруженные пробелы в знаниях. Больше всего выматывает ощущение чёрной дыры: отправляешь резюме, выполняешь задания, тратишь время, а в ответ — ни привета, ни ответа. И ещё одно наблюдение: чем меньше контора, тем больше она иногда мнит себя Google. В условном ООО «Ромашка» легко встретить пять-семь этапов отбора с топологической сортировкой и обходом графов.
Пятое — запасной вариант. Сейчас, по моим наблюдениям, рынок работодателя: кандидатов много, хороших вакансий мало. Если после трёх-четырёх месяцев активного поиска по-прежнему глухо как в танке, я бы посмотрел в сторону смежной специальности. Пусть зарплата ниже, зато устроиться проще. У меня такой запасной вариант — системное администрирование. Не предел мечтаний, но когда заканчиваются деньги, романтика быстро испаряется.
В общем, поиск работы — это отдельный навык, который практически не связан с умением писать хороший код. Одно дело — выйти на рынок подготовленным, с финансовой подушкой, обновлённым резюме и прорешанными алгоритмами. И совсем другое — после увольнения судорожно бегать, тушить пожары и соглашаться на первый попавшийся оффер.
Поэтому готовиться к увольнению лучше, пока тебя ещё не уволили.
Как только в компании начинаются разговоры про оптимизацию, повышение эффективности бизнеса и сложную экономическую ситуацию, я бы задумался. Насколько проект, над которым я работаю, критичен для бизнеса? Некритичный проект могут просто закрыть, а на критичном оставить двух-трёх человек для поддержки. Причём уволить могут довольно быстро, иногда за пару недель.
Первое — финансовая подушка. Я бы начал откладывать деньги, чтобы накопить хотя бы 3–6 месячных окладов. Меня несколько раз увольняли в самые «удачные» периоды: декабрь-январь или летом. В первом случае все на праздниках, во втором — в отпусках. Деньги нужны не только для оплаты коммуналки и питания, но и чтобы не бросаться на первый попавшийся оффер.
Второе — алгоритмы. Причём не видео «Все алгоритмы за два часа», а полноценный курс по решению задач с LeetCode. На собеседовании могут дать две-три задачи по двадцать минут, и это скорее математические головоломки, чем реальное программирование. Я бы не советовал часами биться над каждой задачей самостоятельно. Лучше разобрать существующее решение, воспроизвести его без подсказок и закрепить на похожих задачах. В идеале — 200–300 задач и около полугода подготовки. Рекомендую курс «Подготовка к алгоритмическому собеседованию в BigTech». Самое обидное, что в реальной работе большинство этих трюков практически не используется.
Третье — резюме и собеседования. Сейчас HR активно используют ИИ, поэтому резюме стоит оптимизировать под ключевые слова вакансий, но главное — показать результаты своей работы, а не просто перечислить технологии. Ещё рекомендую посмотреть записи технических собеседований на YouTube и пройти хотя бы одно пробное. На повторение теории вроде асинхронности JS, сборки мусора и работы браузера я бы выделил одну-две недели. Алгоритмы потребуют гораздо больше времени.
Четвёртое — поиск работы. Это отдельная работа, которой я уделял по 4–6 часов в день. Адаптировать резюме, написать сопроводительное письмо, пообщаться с HR, пройти собеседование, подтянуть обнаруженные пробелы в знаниях. Больше всего выматывает ощущение чёрной дыры: отправляешь резюме, выполняешь задания, тратишь время, а в ответ — ни привета, ни ответа. И ещё одно наблюдение: чем меньше контора, тем больше она иногда мнит себя Google. В условном ООО «Ромашка» легко встретить пять-семь этапов отбора с топологической сортировкой и обходом графов.
Пятое — запасной вариант. Сейчас, по моим наблюдениям, рынок работодателя: кандидатов много, хороших вакансий мало. Если после трёх-четырёх месяцев активного поиска по-прежнему глухо как в танке, я бы посмотрел в сторону смежной специальности. Пусть зарплата ниже, зато устроиться проще. У меня такой запасной вариант — системное администрирование. Не предел мечтаний, но когда заканчиваются деньги, романтика быстро испаряется.
В общем, поиск работы — это отдельный навык, который практически не связан с умением писать хороший код. Одно дело — выйти на рынок подготовленным, с финансовой подушкой, обновлённым резюме и прорешанными алгоритмами. И совсем другое — после увольнения судорожно бегать, тушить пожары и соглашаться на первый попавшийся оффер.
Поэтому готовиться к увольнению лучше, пока тебя ещё не уволили.
👍1
Решил на этой неделе сделать небольшую подборку новостей. Весёленькая выдалась неделя: критическая уязвимость PHP, эксперименты с ИИ-презентациями и конференция о будущем разработки. Причём некоторые новости заставляют задуматься, куда вообще движется наша профессия.
1. PHP2Shell — старый PHP не сдаётся
Начну с самой эпичной уязвимости PHP, про которую вы, скорее всего, уже слышали, — PHP2Shell. Пересказывать технические подробности не буду, по ссылке всё подробно и по-русски. На поддерживаемых версиях PHP ничего особенно страшного: обновился и живёшь дальше. Во все основные дистрибутивы экстренно привезли исправления. Казалось бы, проблема решена.
Но PHP — такая вещь, на которой работает куча старья, давно не обновляющегося и уже никогда не обновляемого. К сожалению, у меня такие серверы тоже есть. Обновить их невозможно, а тратить деньги на модернизацию никто не собирается. Например, на серверах с FreeBSD 13 пришлось собирать PHP из исходников. Вот такая современная информационная безопасность.
Заодно посмотрел, сколько у нас в компании сайтов на PHP. Оказалось, примерно 90%, а оставшиеся 10% — всякая экзотика вроде Java, Ruby и NodeJS. Так что PHP живее всех живых, сколько бы его ни хоронили. И, судя по количеству старых корпоративных систем, хоронить его будут ещё очень долго.
2. Как я делал презентацию при помощи ИИ
На этой неделе выступил с докладом «Нальчик и магия Кавказских гор» в Клубе путешественников Тюменского отделения Русского географического общества. Рассказывал о своём путешествии на Кавказ в мае этого года. Если кто пропустил, вот моя статья о поездке: https://vk.ru/@grebenyov-puteshestvie-v-kabardino-balkariu-stranu-chudes.
Но интересно даже не само выступление, а подготовка презентации. Я решил сделать её при помощи ИИ. Мой наивный подход «сделай красиво» быстро упёрся в проблему: ИИ действительно делает красиво, только каждый слайд в совершенно разном стиле. Один получился минималистичным, другой — с кучей декоративных элементов. А мне нужна была единая презентация, а не выставка художественных фантазий нейросети.
Нашёл хорошее видео «Как делать презентации уровня топовых агентств с помощью AI» https://www.youtube.com/watch?v=U2NpS_80mrw. Подход у автора классический — «зыринг и тыринг». Сначала находишь понравившийся дизайн, на его основе создаёшь дизайн-систему, а уже по ней генерируешь отдельные слайды. В результате все слайды получаются в одном стиле. Никакой магии, просто нормальная организация работы.
Кстати, если заметили, обложки для своих постов я теперь генерирую по тому же принципу. Сначала задаю единый визуальный стиль, а потом уже создаю конкретные картинки. Оказалось, что с ИИ всё работает примерно как с обычным дизайнером: если не объяснить, чего именно хочешь, получишь красиво, но совсем не то.
3. Как ИИ меняет разработку ПО
Третья новость. Побывал на офлайн-конференции «Как ИИ меняет разработку ПО». Вот запись конференции. Особенно понравились два доклада, причём совершенно разных: один про рынок труда, другой про автономных ИИ-агентов. Но оба, как оказалось, об одном — о том, как меняется сама профессия программиста.
Первый доклад — «Реальность рынка найма осенью 2026 года». Про трудоустройство в IT прямо сейчас. Скажем так, всё довольно грустно. По приведённым на докладе данным, полноценный поиск работы занимает около полугода, а на одну вакансию может приходиться 2000–3000 резюме. Конкуренция какая-то совершенно нездоровая. Особенно если вспомнить времена, когда программист мог выбирать между несколькими предложениями.
Меняется и сам спрос на специалистов. По словам докладчика, компаниям всё чаще нужны не отдельные фронтендеры или C#-разработчики, а ИИ-инженеры — по сути, фулстеки со знанием AI, способные самостоятельно решить задачу от начала до конца. Пока отдельно держатся бизнес-аналитики и дизайнеры. А вот узкая специализация, похоже, постепенно становится менее привлекательной для работодателей.
1. PHP2Shell — старый PHP не сдаётся
Начну с самой эпичной уязвимости PHP, про которую вы, скорее всего, уже слышали, — PHP2Shell. Пересказывать технические подробности не буду, по ссылке всё подробно и по-русски. На поддерживаемых версиях PHP ничего особенно страшного: обновился и живёшь дальше. Во все основные дистрибутивы экстренно привезли исправления. Казалось бы, проблема решена.
Но PHP — такая вещь, на которой работает куча старья, давно не обновляющегося и уже никогда не обновляемого. К сожалению, у меня такие серверы тоже есть. Обновить их невозможно, а тратить деньги на модернизацию никто не собирается. Например, на серверах с FreeBSD 13 пришлось собирать PHP из исходников. Вот такая современная информационная безопасность.
Заодно посмотрел, сколько у нас в компании сайтов на PHP. Оказалось, примерно 90%, а оставшиеся 10% — всякая экзотика вроде Java, Ruby и NodeJS. Так что PHP живее всех живых, сколько бы его ни хоронили. И, судя по количеству старых корпоративных систем, хоронить его будут ещё очень долго.
2. Как я делал презентацию при помощи ИИ
На этой неделе выступил с докладом «Нальчик и магия Кавказских гор» в Клубе путешественников Тюменского отделения Русского географического общества. Рассказывал о своём путешествии на Кавказ в мае этого года. Если кто пропустил, вот моя статья о поездке: https://vk.ru/@grebenyov-puteshestvie-v-kabardino-balkariu-stranu-chudes.
Но интересно даже не само выступление, а подготовка презентации. Я решил сделать её при помощи ИИ. Мой наивный подход «сделай красиво» быстро упёрся в проблему: ИИ действительно делает красиво, только каждый слайд в совершенно разном стиле. Один получился минималистичным, другой — с кучей декоративных элементов. А мне нужна была единая презентация, а не выставка художественных фантазий нейросети.
Нашёл хорошее видео «Как делать презентации уровня топовых агентств с помощью AI» https://www.youtube.com/watch?v=U2NpS_80mrw. Подход у автора классический — «зыринг и тыринг». Сначала находишь понравившийся дизайн, на его основе создаёшь дизайн-систему, а уже по ней генерируешь отдельные слайды. В результате все слайды получаются в одном стиле. Никакой магии, просто нормальная организация работы.
Кстати, если заметили, обложки для своих постов я теперь генерирую по тому же принципу. Сначала задаю единый визуальный стиль, а потом уже создаю конкретные картинки. Оказалось, что с ИИ всё работает примерно как с обычным дизайнером: если не объяснить, чего именно хочешь, получишь красиво, но совсем не то.
3. Как ИИ меняет разработку ПО
Третья новость. Побывал на офлайн-конференции «Как ИИ меняет разработку ПО». Вот запись конференции. Особенно понравились два доклада, причём совершенно разных: один про рынок труда, другой про автономных ИИ-агентов. Но оба, как оказалось, об одном — о том, как меняется сама профессия программиста.
Первый доклад — «Реальность рынка найма осенью 2026 года». Про трудоустройство в IT прямо сейчас. Скажем так, всё довольно грустно. По приведённым на докладе данным, полноценный поиск работы занимает около полугода, а на одну вакансию может приходиться 2000–3000 резюме. Конкуренция какая-то совершенно нездоровая. Особенно если вспомнить времена, когда программист мог выбирать между несколькими предложениями.
Меняется и сам спрос на специалистов. По словам докладчика, компаниям всё чаще нужны не отдельные фронтендеры или C#-разработчики, а ИИ-инженеры — по сути, фулстеки со знанием AI, способные самостоятельно решить задачу от начала до конца. Пока отдельно держатся бизнес-аналитики и дизайнеры. А вот узкая специализация, похоже, постепенно становится менее привлекательной для работодателей.
PHP2Shell.ru
PHP2Shell: критическая уязвимость PHP — как защититься до патча
CVE-2026-109618 и CVE-2026-109619, CVSS 9.2. Ручные меры защиты и где брать патчи PHP.
❤1👍1
Как я понял, заметно лучше ситуация у Data Scientist и MLOps-инженеров. Это уже специалисты, которые умеют не просто отправлять текст в нейросеть и получать ответ, а работать с моделями, данными, развёртыванием и эксплуатацией ИИ внутри закрытой корпоративной инфраструктуры. Но есть нюанс: таких специалистов рынку требуется относительно немного. Так что массовым спасением для программистов это направление тоже не назовёшь.
Второй доклад — «Human In The Loop — или как жить с нейрослопом». Автор рассказывал про автономных ИИ-агентов на примере DeepSeek Harness. Настроил систему, раздал задачи по своим домашним pet-проектам, оставил работать, а утром получил 700 новых Pull Request (PR). Вроде бы красота: пока ты спишь, ИИ программирует. Только вот теперь попробуй разобраться в этих 700 PR и понять, что он там вообще напрограммировал. Автоматизировали написание кода, а вот что делать с результатом — отдельный вопрос.
В ходе дискуссии пришли к интересному выводу: главное не количество новых коммитов или PR, а Time to Market — время от появления идеи новой фичи до её выхода в production. Можно генерировать сотни PR в день, но если они неделями висят на проверке, никакого ускорения разработки не получится. Просто бутылочное горлышко переедет в другое место.
Поэтому ускорять нужно весь процесс: от бизнес-аналитика и постановки задачи до тестирования, ревью и DevOps. Иначе получится забавная ситуация: программисты при помощи ИИ клепают уйму новых фич, а до прода они добираются с прежней скоростью. Только теперь ещё и приходится разгребать горы автоматически сгенерированного кода.
И вот что интересно. Все три новости, несмотря на совершенно разные темы, об одном и том же. Технологии меняются, ИИ ускоряет работу, появляются новые инструменты, а старые системы никуда не исчезают. И в итоге самое сложное по-прежнему не написать код или сгенерировать красивую картинку, а довести всё это до рабочего результата.
Можно сгенерировать 700 PR за ночь. Но кто-то всё равно должен разобраться, что из этого действительно нужно, как оно работает и кто потом будет это поддерживать.
Второй доклад — «Human In The Loop — или как жить с нейрослопом». Автор рассказывал про автономных ИИ-агентов на примере DeepSeek Harness. Настроил систему, раздал задачи по своим домашним pet-проектам, оставил работать, а утром получил 700 новых Pull Request (PR). Вроде бы красота: пока ты спишь, ИИ программирует. Только вот теперь попробуй разобраться в этих 700 PR и понять, что он там вообще напрограммировал. Автоматизировали написание кода, а вот что делать с результатом — отдельный вопрос.
В ходе дискуссии пришли к интересному выводу: главное не количество новых коммитов или PR, а Time to Market — время от появления идеи новой фичи до её выхода в production. Можно генерировать сотни PR в день, но если они неделями висят на проверке, никакого ускорения разработки не получится. Просто бутылочное горлышко переедет в другое место.
Поэтому ускорять нужно весь процесс: от бизнес-аналитика и постановки задачи до тестирования, ревью и DevOps. Иначе получится забавная ситуация: программисты при помощи ИИ клепают уйму новых фич, а до прода они добираются с прежней скоростью. Только теперь ещё и приходится разгребать горы автоматически сгенерированного кода.
И вот что интересно. Все три новости, несмотря на совершенно разные темы, об одном и том же. Технологии меняются, ИИ ускоряет работу, появляются новые инструменты, а старые системы никуда не исчезают. И в итоге самое сложное по-прежнему не написать код или сгенерировать красивую картинку, а довести всё это до рабочего результата.
Можно сгенерировать 700 PR за ночь. Но кто-то всё равно должен разобраться, что из этого действительно нужно, как оно работает и кто потом будет это поддерживать.
👍1
На работе очередной аврал. Нашли критические уязвимости в Node.js, и обычное обновление превратилось в небольшой технический цирк. А параллельно выяснилось, что исправить неправильную дату в тысячах телефонных разговоров сложнее, чем собрать Node.js из исходников на старой FreeBSD. Причём каждая следующая проблема вылезала ровно после того, как я разбирался с предыдущей. Но обо всём по порядку.
Вот статья про уязвимости: https://habr.com/ru/companies/otus/news/1097666/ Если кратко, нужно переходить на Node.js 22.23.4 или 24.22.1. В нашей компании из сисадминов на Node.js пишу только я. И тут неожиданно выяснилось, что почти за два года я написал очень приличное количество скриптов и сервисов. Теперь всё это хозяйство пришлось собирать в кучку и обновлять.
С Docker-приложениями всё оказалось просто: поправил Dockerfile, пересобрал образ, запустил на сервере — красота. На некоторых серверах с Debian 13 нужной версии Node.js в репозиториях нет, но и это решаемо: скачал готовый бинарник, настроил симлинки. А вот FreeBSD 13 — совсем другая история. Новые пакеты уже не выходят, поэтому полез в исходники и начал собирать Node.js вручную.
Исходники пришлось брать из репозитория FreeBSD 14, а потом молиться, чтобы всё собралось. Одна компиляция Node.js 22 занимает около часа. Часть проектов после перехода с Node.js 20 на 22 собираться отказалась, особенно пришлось повозиться с TypeScript. Версия теперь фактически «прибита гвоздями»: выйдет 22.23.5 — повторяй весь цирк заново.
Параллельно воевал с Яндексом. У нас есть ИИ-система, которая распознаёт телефонные разговоры и оценивает, как менеджеры отрабатывают скрипты продаж. Звонки из АТС загружает моя программа. Всё работало, пока я не заметил две ошибки: один и тот же оператор записан как «Иванов» и «Ivanov», а дата начала звонка уехала в другую тайм-зону. Программу исправил, новые записи стали нормальными. Но старых-то тысячи!
Самое очевидное решение — удалить всё и загрузить заново. Только распознавание у Яндекса платное, и сумма за повторную обработку получается ну очень интересная. Полез в API: метаданные менять вроде бы можно. Фамилия оператора обновляется без вопросов, а дата — никак. Пробовал разные форматы, переписывал запросы. Яндекс бодро отвечает, что всё хорошо, но сама дата остаётся прежней. Техподдержка подтвердила: без повторной загрузки записи её не изменить.
Ладно, хотя бы фамилии исправлю. Написал свою мегапрограмму, запустил в 10 потоков — и тут прилетел
За неделю получил два урока. Первый: Docker сильно облегчает жизнь, когда поддерживаешь много сервисов на разных системах. Второй: ошибка в данных может обойтись дороже, чем ошибка в коде. Код исправил, перезапустил — готово. А тысячи уже распознанных разговоров так просто не поправишь. Ошибку совершить легко. А вот последствия иногда исправлять технически можно, но экономически совершенно бессмысленно.
Вот статья про уязвимости: https://habr.com/ru/companies/otus/news/1097666/ Если кратко, нужно переходить на Node.js 22.23.4 или 24.22.1. В нашей компании из сисадминов на Node.js пишу только я. И тут неожиданно выяснилось, что почти за два года я написал очень приличное количество скриптов и сервисов. Теперь всё это хозяйство пришлось собирать в кучку и обновлять.
С Docker-приложениями всё оказалось просто: поправил Dockerfile, пересобрал образ, запустил на сервере — красота. На некоторых серверах с Debian 13 нужной версии Node.js в репозиториях нет, но и это решаемо: скачал готовый бинарник, настроил симлинки. А вот FreeBSD 13 — совсем другая история. Новые пакеты уже не выходят, поэтому полез в исходники и начал собирать Node.js вручную.
Исходники пришлось брать из репозитория FreeBSD 14, а потом молиться, чтобы всё собралось. Одна компиляция Node.js 22 занимает около часа. Часть проектов после перехода с Node.js 20 на 22 собираться отказалась, особенно пришлось повозиться с TypeScript. Версия теперь фактически «прибита гвоздями»: выйдет 22.23.5 — повторяй весь цирк заново.
apt update && apt upgrade -y тут не спасёт. После такого я стал фанатом Docker. Жаль, на FreeBSD его нет.Параллельно воевал с Яндексом. У нас есть ИИ-система, которая распознаёт телефонные разговоры и оценивает, как менеджеры отрабатывают скрипты продаж. Звонки из АТС загружает моя программа. Всё работало, пока я не заметил две ошибки: один и тот же оператор записан как «Иванов» и «Ivanov», а дата начала звонка уехала в другую тайм-зону. Программу исправил, новые записи стали нормальными. Но старых-то тысячи!
Самое очевидное решение — удалить всё и загрузить заново. Только распознавание у Яндекса платное, и сумма за повторную обработку получается ну очень интересная. Полез в API: метаданные менять вроде бы можно. Фамилия оператора обновляется без вопросов, а дата — никак. Пробовал разные форматы, переписывал запросы. Яндекс бодро отвечает, что всё хорошо, но сама дата остаётся прежней. Техподдержка подтвердила: без повторной загрузки записи её не изменить.
Ладно, хотя бы фамилии исправлю. Написал свою мегапрограмму, запустил в 10 потоков — и тут прилетел
rate limit. Яндекс не любит слишком много запросов подряд. Пришлось снова править код, ограничивать интенсивность и запускать обработку заново. В итоге фамилии исправил, а старые даты остались как есть: при аналитике за сентябрь и октябрь теперь нужно учитывать разницу во времени.За неделю получил два урока. Первый: Docker сильно облегчает жизнь, когда поддерживаешь много сервисов на разных системах. Второй: ошибка в данных может обойтись дороже, чем ошибка в коде. Код исправил, перезапустил — готово. А тысячи уже распознанных разговоров так просто не поправишь. Ошибку совершить легко. А вот последствия иногда исправлять технически можно, но экономически совершенно бессмысленно.
Хабр
В Node.js исправили 10 уязвимостей — включая возможное RCE в Brotli
21 октября команда Node.js выпустила обновления безопасности для веток 22.x, 24.x и 26.x. Разработчики закрыли десять уязвимостей, четыре из которых получили высокий уровень опасности. Среди наиболее...
❤1👍1