LINUX.ORG.RU

Ant: создание среды выполнения JavaScript за один месяц

 , , , ,


1

4

Это перевод поста building a javascript runtime in one month от второго января 2026 года.

TLDR: Я создал Ant, небольшую (2 МБ) среду выполнения JavaScript. Полный исходный код, а также тесты и документацию можно найти на моем github.

Когда я начал этот проект в начале ноября, у меня была простая идея: а что, если я смогу создать движок JavaScript, достаточно маленький, чтобы его можно было встроить в программу на C, но достаточно полный, чтобы на нём можно было запускать реальный код? Что-то, что можно было бы распространять, не таская с собой сотни мегабайт V8 или Node. Я уже пробовал это раньше с минимальными копиями Deno, но этого было недостаточно.

Я не думал, что это займет месяц. Я не думал, что это будет возможно за месяц. Но вот в чём дело – когда создаёшь что-то без сроков, ты просто продолжаешь работать.

Первая неделя была режимом выживания

Я учился в режиме реального времени, то есть методом проб и ошибок, и по ходу дела исправлял все ошибки. Работа началась с абсолютных основ. Добавление числовых операций, встроенных строковых функций и даже элементарной системы модулей commonjs. Каждый коммит — маленькая победа над пустотой.

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

Знаете ли вы, что автоматическая вставка точки с запятой является фактической частью спецификации, с которой вам приходится иметь дело?

Знаете ли вы, что биндинг this меняется в зависимости от контекста, и что предварительное объявление означает, что переменные, объявленные с помощью var, существуют до того, как им присваивается значение? Даже window.window.window является действительным…

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

typedef uint64_t jsval_t;

Каждое значение JavaScript в среде выполнения представляет собой одно 64-разрядное целое число. NaN-боксирование. Спецификация IEEE 754 с плавающей запятой имеет странный пробел: существует 2^53 возможных значений NaN, и большинство из них никогда не используются. Поэтому я их украл :3.

Если значение выглядит как NaN при интерпретации битов как double, то экспонента и мантисса следуют заданному вами шаблону, и вы можете закодировать тег в этих битах. У вас есть место для указателя и типа. Вы упаковываете ссылку на объект и тег в 64 бита. Внезапно каждое значение JavaScript помещается в одно машинно-ориентированное слово.

Проверки на этапе компиляции доказывают это:

_Static_assert(sizeof(double) == 8, "NaN-boxing requires 64-bit IEEE 754 doubles");
_Static_assert(sizeof(uint64_t) == 8, "NaN-boxing requires 64-bit integers");
_Static_assert(sizeof(double) == sizeof(uint64_t), "double and uint64_t must have same size");

Это стало основой того, как среда выполнения представляет каждое отдельное значение. Каждое число, каждый объект, каждая строка, каждая функция, каждое promise, каждый аргумент, каждая область действия. Всё является одним из них. Никаких дискриминируемых объединений, никаких vtable, никаких выделений кучи для метаданных. Только биты. Потребовались дни итераций, чтобы дойти до этого, но как только это заработало, всё остальное стало быстрее. Значения NaN и Infinity имели свои собственные проблемы, но небольшая перестановка боксирования помогла их исправить.

Примерно на четвёртый день я заставил работать переменные. На пятый день – функции. На шестой день я смог запустить циклы. Первые коммиты разбросаны. Вскоре после этого я добавил стрелочные функции, iife, опциональное цепочечное соединение и даже операторы nullish coalescing. Я перескакивал между функциями, потому что просто читал MDN, добавляя вещи по мере того, как вспоминал об их существовании.

Катастрофа со сборкой мусора

И тогда это случилось: управление памятью.

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

Я очень мучился на этом этапе. Ручной free-list GC отключался и включался сотни раз, и каждая попытка ломала ещё одну важную часть. Были дни, когда я был явно разочарован. В 3 часа ночи я занимался отладкой, пытаясь понять, почему стеки сопрограмм не защищались должным образом? Почему происходила утечка памяти? Почему все ломалось, когда я добавлял поддержку json?

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

Promises были совсем другим делом

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

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

Если посмотреть, то в коммитах видно, как это было сложно: «async promise pushback», «segfault when event loop empty», «prevent dead task from blocking». Это проблемы, которые невозможно предвидеть, пока не дойдёшь до середины реализации.

Сложность заключается в том, что promises JavaScript не могут быть простыми. Они должны обрабатывать цепочки .then(), они должны правильно отклонять, они должны работать с асинхронными функциями, которые являются всего лишь синтаксическим сахаром над генераторами, которые являются всего лишь синтаксическим сахаром над promises и обратными вызовами.

Примерно на десятый день я подключил minicoro для поддержки сопрограмм. Это решение, вероятно, спасло весь проект. Minicoro – элегантная реализация. Вы определяете сопрограммы на основе стека и позволяете системе переключаться между ними. Как только у меня появились сопрограммы, я смог реализовать асинхронную работу.

typedef struct coroutine {
  struct js *js;
  coroutine_type_t type;
  jsval_t scope;
  jsval_t this_val;
  jsval_t awaited_promise;
  jsval_t result;
  jsval_t async_func;
  jsval_t *args;
  int nargs;
  bool is_settled;
  bool is_error;
  bool is_done;
  jsoff_t resume_point;
  jsval_t yield_value;
  struct coroutine *prev;
  struct coroutine *next;
  mco_coro* mco;
  bool mco_started;
  bool is_ready;
} coroutine_t;

В этой структуре отслеживалось всё, что касалось асинхронного выполнения: область действия, значение this, ожидаемое promise, наличие ошибок. Затем мне нужно было только запланировать эти действия и управлять циклом событий.

Как только у меня появились сопрограммы, promises стали реальностью. Цепочка .then() заработала, await действительно приостанавливала выполнение и возобновляла его позже, асинхронная сторона среды выполнения начала складываться. Я начал добавлять надлежащую обработку promises, и все встроенные функции Promise появились после этого, и они появились быстро, потому что сложная часть была решена.

Странные крайние случаи JavaScript

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

Я решаю их одну за другой. Обработка замороженных/запечатанных объектов, добавление поддержки неконфигурируемых свойств, исправление деструктуризации в десятый раз, добавление поддержки доступа к свойствам с помощью getter и setter. Каждый день сталкиваюсь с новыми крайними случаями. Иногда приходится исправлять несколько ошибок за день, потому что я что-то реализую, запускаю тесты на соответствие, нахожу три ошибки, исправляю их, а потом нахожу еще пять ошибок, о которых я не знал.

Знаете ли вы, сколько способов доступа к цепочке прототипов предоставляет JavaScript? __proto__, Object.getPrototypeOf(), Object.setPrototypeOf() и внутренний слот [[Prototype]]. Вы должны обрабатывать все из них, и они должны правильно взаимодействовать друг с другом. Обманчиво короткий коммит под названием «use descriptor tables for getters/setters/properties» представляет собой недели работы, чтобы правильно обработать это.

Деструктуризация казалась простой: const [a, b] = arr. Но как быть с разрежёнными массивами? С перечисляемыми свойствами объектов? С вложенной деструктуризацией? Со значениями по умолчанию? Параметрами ...rest? Каждое исправление, кажется, приводит к появлению другого, и каждое из них – это «кроличья нора» крайних случаев.

Тесты на соответствие были жестокими в лучшем смысле этого слова. Каждый раз, когда я их запускал, они выявляли ошибки, о которых я даже не подозревал. Затем я исправлял их и переходил к следующему пункту. Этот цикл повторялся десятки раз.

Во второй половине всё стало странным

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

Операции с файловой системой, утилиты для работы с путями файлов, модуль url и всеми любимый встроенный http-сервер, который сделал Bun популярным. Внезапно с Ant стали работать настоящие программы. Вы могли бы написать веб-сервер, используя только это:

import { join } from 'ant:path';
import { readFile } from 'ant:fs';
import { createRouter, addRoute, findRoute } from 'rou3';

const router = createRouter();

addRoute(router, 'GET', '/status/:id', async c => {
  await new Promise(resolve => setTimeout(resolve, 1000));

  const result = await Promise.resolve('Hello');
  const name = await readFile(join(import.meta.dirname, 'name.txt'));

  const base = '{{name}} {{version}} server is responding with';
  const data = { name, version: Ant.version() };

  return c.res.body(`${base.template(data)} ${result} ${c.params.id}!`);
});

async function handleRequest(c) {
  console.log('request:', c.req.method, c.req.uri);
  const result = findRoute(router, c.req.method, c.req.uri);

  if (result?.data) {
    c.params = result.params;
    return await result.data(c);
  }

  c.res.body('not found: ' + c.req.uri, 404);
}

console.log('started on http://localhost:8000');
Ant.serve(8000, handleRequest);
$ ant examples/server/server.js
started on http://localhost:8000

$ curl http://localhost:8000/status/world
Ant 0.3.2.6 server is responding with Hello world!

Это настоящий JavaScript, работающий в Ant: async/await, ввод-вывод файлов, HTTP, маршрутизация с параметрами, cетевые операции, операции со строками.

Темп работы увеличился. С каждым днём я становился всё более уверенным, работал быстрее, исправлял больше ошибок, добавлял больше функций. Затем наступила «неясная, но необходимая фаза». Я добавил поддержку прокси, рефлексию, символы и даже частные поля и методы в классах. Каждая из этих функций является функцией ECMA6+, которую почти никто не использует, но спецификация гласит, что они существуют, поэтому они должны работать.

Одна из моих любимых – атомарные операции:

const sharedBuffer = new SharedArrayBuffer(256);

const int32View = new Int32Array(sharedBuffer);
Atomics.store(int32View, 0, 42);
const value = Atomics.load(int32View, 0);
console.log('stored 42, loaded:', value);

Atomics.store(int32View, 1, 10);
const oldValue = Atomics.add(int32View, 1, 5);
console.log('old value:', oldValue);

Atomics.store(int32View, 2, 100);
const result = Atomics.compareExchange(int32View, 2, 100, 200);
console.log('exchanged, new value:', Atomics.load(int32View, 2));
$ ant examples/atomics.js
stored 42, loaded: 42
old value: 10
exchanged, new value: 200
Последняя неделя была просто домино

Как только ядро Ant заработало, сборка мусора стала стабильной, а функции promises заработали, всё остальное стало на свои места: незначительные проблемы были устранены, добавлены недостающие методы, решены крайние случаи.

Мне удалось повторно добавить проверку длины массива и даже исправить недействительность кеша свойств для объектов. Я изучил возможность улучшения производительности хеширования, обнаружил «кроличью нору» сложных алгоритмов и последствий для безопасности. Всё это потому, что я дорабатывал то, что уже работает.

К 28-му дню я дорабатывал то, что действительно работало. Полная среда выполнения JavaScript с поддержкой async/await, надлежащим управлением памятью, сетевыми функциями, вводом-выводом файлов и полной совместимостью со спецификациями ES1-ES5, а также случайным набором ещё более новых функций.

Я даже «вспомнил» включить оптимизацию LTO и другие флаги компилятора после того, как мне напомнили 😅.

Что случилось на самом деле

Один месяц. Ant теперь существует как среда выполнения JavaScript, которая:

  • Проходит все тесты на соответствие ES1–ES5 из набора тестов javascript-zoo. Полное соответствие 25-тилетним спецификациям JavaScript.
  • Реализует async/await с правильной обработкой promises и микрозадачами.
  • Имеет сборщик мусора, который действительно работает без утечек памяти.
  • Может запускать веб-серверы с использованием libuv (так же, как Node) для работы c сетью.
  • Может вызывать библиотеки через ffi следующим образом:
import { dlopen, suffix, FFIType } from 'ant:ffi';

const sqlite3 = dlopen(`libsqlite3.${suffix}`);

sqlite3.define('sqlite3_libversion', {
  args: [],
  returns: FFIType.string
});

console.log(`version: ${sqlite3.sqlite3_libversion()}`);
$ ant examples/ffi/basic/sqlite.js
version: 3.43.2
  • Может читать и записывать файлы, а также выполнять асинхронный ввод-вывод.
  • Имеет правильную работу областей видимости, предварительного объявления и затенения переменных.
  • Поддерживает классы, стрелочные функции, деструктуризацию, операторы распространения, шаблонные литералы, опциональное цепочечное соединение.
  • Обрабатывает необычные крайние случаи, о которых большинство людей не задумывается: присвоение __proto__, дескрипторы свойств, неконфигурируемые свойства, замороженные и запечатанные объекты. См. tests/proto.js.
  • Реализует систему модулей ES, которая может импортировать и экспортировать код.
  • Поддерживает символы, прокси, рефлексию, weakmaps, weaksets, maps, sets.
  • Поддерживает разделяемую память и атомарные операции для параллельного программирования. Прочитав это, вы понимаете, что перед вами полноценная среда выполнения JavaScript, а не просто игрушка.
Цена

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

В некоторые дни я работал более 10 часов подряд. В некоторые дни было более 20 коммитов. Проект не замедляется. Он ускоряется. С каждым днем я становлюсь более уверенным, двигаюсь быстрее, исправляю больше ошибок, добавляю больше функций.

В конце я сталкиваюсь с крайними случаями, которые требуют чтения спецификациё ECMAScript, понимания того, что делает V8, проверки того, как другие движки обрабатывают некоторые странные крайние случаи. Улучшение подсчета символов, оптимизация классов и даже перенос внутренних свойств в слоты (как это делает V8). Это оптимизация производительности и улучшения архитектуры, которые должны происходить только после стабилизации кодовой базы. За исключением того, что всё это происходит на прошлой неделе, потому что основа прочная, и у меня есть пропускная способность.

После выпуска: этап оптимизации

Первый релиз состоялся 26 ноября. Затем наступила тишина. Обычная тишина, которая наступает после выпуска чего-либо. Затем, около 20 декабря, работа возобновилась.

На этот раз всё было по-другому. Среда выполнения работала. Она прошла тесты. Но всегда есть что оптимизировать. Xctrace показал, что действительно имело значение. Коммиты в конце декабря и начале января показывают следующую схему: обнаружить узкое место, исправить его, измерить улучшение.

Я начал с добавления аллокатора арены для типизированных массивов. Раньше типизированные массивы были разбросаны по всей куче. Поэтому я их объединил, чтобы ускорить выделение памяти и улучшить локальность кеша.

Затем я перешёл на использование таблиц дескрипторов для геттеров/сеттеров/свойств. Вместо отдельных выделений для каждого дескриптора свойства я сгруппировал их в таблицы. Меньше выделений, меньше преследований указателей.

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

Я люблю таблицы диспетчеризации. Я перешел на использование вычисляемого goto для ffi, json и других. Это позволяет процессору переходить непосредственно к нужному обработчику. Устраняется ветвление, устраняется поиск.

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

Где-то здесь появилось сравнение с Node. Запустить те же тесты. Как справляется Ant? Результаты стали выглядеть хорошо. Очень хорошо. Настолько хорошо, что начинаешь задаваться вопросом, смогу ли я действительно превзойти Node в чём-то?

Оптимизация Ant создавала снимки рабочих версий. Если я что-то ломал при оптимизации, у меня была точка, к которой я мог вернуться. Постепенный прогресс. Каждая фиксация была немного быстрее предыдущей. Некоторые оптимизации работали, некоторые — нет. Но схема оставалась прежней: профилирование, оптимизация, измерение, коммит.

Затем последовали улучшения в работе сборщика мусора. Интеграция BDWGC работала в течение первого месяца, но где-то на этапе оптимизации она была отключена. Среда выполнения просто теряла память. Я снова добавил отложенную сборку мусора и раскомментировал большую часть старого GC.

Но не по-старому, это был компактный сборщик мусора с маркировкой-копированием, который фактически дефрагментировал память. Старый сборщик мусора работал в неподходящее время, вызывая задержки в «горячих точках». Поэтому я сделал его откладываемым, собирающим между логическими единицами работы, с отслеживанием прямых ссылок, чтобы объекты могли перемещаться в памяти без нарушения указателей. Таким образом, сборщик мусора вернулся, но стал умнее. Он откладывает сборку до тех пор, пока не станет безопасно сделать паузу, а когда запускается, он уплотняет кучу.

Почему это случилось

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

Проект существует, потому что что-то во мне решило, что он должен существовать, и не остановилось, пока это не произошло.

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

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

★★★★★

Проверено: maxcom ()
Последнее исправление: dataman (всего исправлений: 1)

Название неудачное, потому что уже занято. Ant - это система сборки кода на Java.

seiken ★★★★★
()
Ответ на: комментарий от seiken

Название неудачное, потому что уже занято. Ant - это система сборки кода на Java.

Java, JavaScript - какая разница 🤷

п.с. вообще тоже про него подумал

frunobulax ★★★★
()

движок JavaScript, достаточно маленький, чтобы его можно было встроить в программу на C

Кощунство какое-то.

Zhbert ★★★★★
()

Похоже setTimeout неправильно передаёт/обрабатывает параметр задержки.

setTimeout(() => console.log(1), 1000)

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

Проверил на сборке из master и скаченной с github.

Ja-Ja-Hey-Ho ★★★★★
()

Простая арифметика грит, что автор писал 70 строк в час, если учесть, что он при этом вообще никаких исследований и отладок не проводил. Общая статистика даж веселее:

theMackabu <theMackabu@gmail.com>:
	  insertions:    125035	(100%)
	  deletions:     35007	(100%)
	  files:         2833	(100%)
	  commits:       710	(100%)
	  lines changed: 160042	(100%)
	  first commit:  Wed Nov 19 14:28:06 2025 -0800
	  last commit:   Wed Feb 4 20:02:51 2026 -0800
2000 строчек в день, 270 строчек в час. Мне бы так.
https://bykozy.me/static/ant_repo_stack_plot.png

Логичное замечание при чтении кода — кто эту срань после автора будет поддерживать?
https://bykozy.me/static/ant_diff.png
https://bykozy.me/static/ant_diff2.png

У меня тоже была идея запилить что-то своё новомодными ИИ, но каждый раз возникало понимание, что это будет очередной огрызок на 90% такой же, как аналогичные огрызки.

Мне нейросетка подсказала, что

Offset-based memory model (jsoff_t offsets into contiguous mem buffer)
Tagged value representation (mkval(T_OBJ, ...))
Property storage using offset chains

являются шаблонными приёмами, применёнными в куче других JS-интерпретаторов.
Например:
https://github.com/cesanta/elk/blob/a9bb85619c5cddf49dfa8bdf529770fc9943a7fd/...
сравните с
https://github.com/theMackabu/ant/blob/42e2fdb00d8b5ae39cd07a96caf77627e418ab...

Даже константы внутренние не поменял.

По этой причине в разделе «the price» автор скромно умалчивает о счёте от провайдера, который автор получил через месяц интенсивной генерации кода ИИ.

К слову, я недавно обосрал парочку аналогичных проектов, с которыми я недавно столкнулся:
https://bykozy.me/blog/state-of-decay-in-self-hosted-commenting/
У Artalk картина была аналогичная — 90% кода написано в одно рыло за несколько месяцев, и потом проект встаёт колом на одном месте...

Не знаю, грустно как-то это.

byko3y ★★★★
()
Последнее исправление: byko3y (всего исправлений: 3)
Ответ на: комментарий от byko3y

Статью писал ИИ, почему б ему и программку не сделать.

Irma ★★★
()

Не хило так человек заморочился.

P.S. Для сравнения, метациклический интерпретатор схемы занимает строк триста кода и пишется за пару вечеров человеком, который только что узнал слово «метациклический интерпретатор».

ugoday ★★★★★
()
Ответ на: комментарий от seiken

Тоже такая же первая мысль при прочтении новости.

«ну куда он лезет, если даже базу не знает»

Saakx
()
Последнее исправление: Saakx (всего исправлений: 1)
Ответ на: комментарий от ugoday

Не хило так человек заморочился.
P.S. Для сравнения, метациклический интерпретатор схемы занимает строк триста кода и пишется за пару вечеров человеком, который только что узнал слово «метациклический интерпретатор».

При чём тут человек? Код писал ИИ, причём, либо прямо скопировав elk.js, либо косвенно нейросеть написала копию elk.js, как эталонного интерпретатора:
https://bykozy.me/static/elk_vs_ant.png

Вот зачем он столько токенов потратил на релизацию методов Array и String — это вопрос... Впрочем, это новый уровень «домашних работ» в 2026 году.

byko3y ★★★★
()

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

anonymous_sama ★★★★★
()
Ответ на: комментарий от anonymous_sama

Но конечно такого человека интересно было бы в штате иметь среди разработчиков.

«Такого» — это какого? Сейчас кодерские конторы воют, потому что все джуны поголовно вайбкодят и перегружают сеньоров ревью вот такого говнокода. Джун тратит 5 минут на генерацию коммита на 500 строк — сеньор тратит час на его ревью. Я уже имел честь с такими работать.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Поставьте веб-камеры. Пусть покажет как работал: в раздражении ходил из угла в угол, задумчиво курил, глядя в окно, нервыми руками комкал листы, исчерченные блок-схемами и диаграммами, плакал.

ugoday ★★★★★
()
Ответ на: комментарий от ugoday

Достаточно просто посадить на ревью и попросить объяснить, что делает его код. Не самому разбираться, а пусть он объясняет.

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

byko3y ★★★★
()
Ответ на: комментарий от Beewek

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

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Такого кто знаком с более низким уровнем, конкретно про автора статьи в оригинале хз, но звучит как он очень хорошо разобрался как Node.js работает на низком уровне, а соответственно может в теории найти узкие места в процессах или рассказать как что-то работает намного глубже обычного сеньера.

anonymous_sama ★★★★★
()
Ответ на: комментарий от anonymous_sama

соответственно может в теории найти узкие места в процессах или рассказать как что-то работает намного глубже обычного сеньера.

Нет, лол. Дать ИИ команду «ну сделать там чота, ну типа как в ноде оно оптимизирововано» — это не «в теории найти узкие места в процессах».

byko3y ★★★★
()
Ответ на: комментарий от byko3y

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

anonymous_sama ★★★★★
()
Последнее исправление: anonymous_sama (всего исправлений: 1)
Ответ на: комментарий от anonymous_sama

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

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

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Вообще-то это по нынешним временам довольно необычно. Сейчас все наоборот, хвастаются тем, что им помогал ии. Может всё же сам?

Beewek ★★★
()
Ответ на: комментарий от Beewek

Может я что-то не понимаю в жизни людей в 2026, но я чот не вижу людей, бегающих с криками: «смотрите, какой у меня классное резюме, написанное нейросетью! А ещё на собеседовании я такую хорошую неросеть применяю! И кодить я не умею — да и зачем, если нейросеть напишет лучше?». Ну то есть на линкедине и реддите такие посты есть, но эти же люди не пишут в резюме об этом прямо — факт применения ИИ очень часто скрывается и замалчивается в ключевых моментах.

Есть люди, которые идут на интеграторов ИИ — они тоже могут говорить о том, как классно умеют применять ИИ, но частенько забывают уточнить, что это ИИ применяют не они, а Aider, которым они задают промт «сделай мне репозиторий с проетом на питоне, который генерирует картинки по записям из БД и по расписанию отправляет сообщения с картинками в группы в телеграмме». Опять же, здесь если заказчик не полный лопух, то он спросит «а вот если у вас что-то сломалось, или информация нестандартная — вы что делать будете?» — в этом моменте эксперты по ИИ могут очень внезапно закончиться. А ведь именно в проблемных местах и нужен эксперт — шаблонную штуку можно набросать в Zapier, для этого «иксперт» не нужен.

byko3y ★★★★
()
Ответ на: комментарий от byko3y

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

Ну тогда все честно, ИИ агент написал код, потом написал статью как он это сделал)

Честно говоря не верится, что такое можно сделать за месяц самому, без ИИ.

goingUp ★★★★★
()
Ответ на: комментарий от byko3y

Нормальные работники этим кодерским конторам не нужны? БЯМ использую только для помощи в обучении. Код люблю и предпочитаю руками писать. c:

verdammter_katze
()
Ответ на: комментарий от goingUp

Ну тогда все честно, ИИ агент написал код

Бедолага, ночами не спал. Я сначала подумал, что это dataman сам лично написал (потому что читаю жопой снизу вверх). Удивился какие таланты на лоре пропадают.

bread
()
Ответ на: комментарий от bread

Блин, на хабре тоже был чел, который несколько сотен, если не тыщь долларов потратил на проект солидного размера, полностью вайбкоденный.

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

byko3y ★★★★
()
Ответ на: комментарий от byko3y

От статьи определённо веет бредогенераторами. Но я, пока не посмотрел твои комменты, подумал что это переводчик виноват. Видимо не переводчик, или не только он.

dataman, а ты сам переводил или перевод тоже ии?

firkax ★★★★★
()
Ответ на: комментарий от goingUp

Честно говоря не верится, что такое можно сделать за месяц самому, без ИИ.

А по-моему тупой js-интерпретатор можно и быстрее написать (я, правда, не пробовал и пока не планирую), но нужен ли он? Тормозить же будет везде, нужны оптимизаторы и jit.

firkax ★★★★★
()

большой труд, но..

  • edge cases -> краевые случаи
  • production-grade -> промышленного уровня

и что-то еще было… А, переводы не нужны, давайте не будем делать «еще один хабр», нам «еще одного опеннета» хватает 🤣

aol ★★★★★
()
Ответ на: комментарий от firkax

А по-моему тупой js-интерпретатор можно и быстрее написать (я, правда, не пробовал и пока не планирую), но нужен ли он? Тормозить же будет везде, нужны оптимизаторы и jit

Сложность реализации оптимизаторов и jit возникает из-за того, что JS код часто пишется как питон: «я установил весь интерпретатор — я буду использовать все функции интерпретатора». Тот же Дуглас Крокфорд давно гооворит, что как минимум половину фич JS нужно из языка выкинуть.

Например, сколько времени у команды V8 понадобилось потратить, чтобы сделать полную поддержку цикла for...in.

А какое-то люто кастрированное подмножество очень просто сделать — сам первый JS был за 10 дней сделан так-то.

Вот эти все mjs и elk.js нужны как раз чтобы не тащить всю эту парашу в легковесные продукты, но при этом реализовать поддержку знакомого ЯП для необучаемых пользователей. Но JS в микроконтроллерах?... Блин, вам совсем нечем заняться?

byko3y ★★★★
()
Ответ на: комментарий от aol

edge cases -> краевые случаи

Граничные случаи, специфичные ситуации.

production-grade -> промышленного уровня

Западное айти любит дуть щёки, притворяясь отраслью, которая что-то производит, а не надувает финансовые пузыри. Но в СНГ ситуация попроще. Production grade — «готовый к эксплуатации» или «полноценный продукт».

byko3y ★★★★
()
Ответ на: комментарий от byko3y

По-моему js-интерпретаторы нужны чтобы интерпретировать всякий мусор, пришедший из веба (и у тебя нет выбор какие конструкции они там будут использовать). А если пишешь сам то лучше начать с выкидывания всего js целиком.

Например, сколько времени у команды V8 понадобилось потратить, чтобы сделать полную поддержку цикла for...in.

Не вижу в чём проблема. Ну кроме скорости выполнения, разумеется.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от firkax

По-моему js-интерпретаторы нужны чтобы интерпретировать всякий мусор, пришедший из веба (и у тебя нет выбор какие конструкции они там будут использовать). А если пишешь сам то лучше начать с выкидывания всего js целиком.

Бро, разреши пожать тебе руку. Но, к сожалению, в индустрии уже написано запредельное число софта на JS без малейшей технической причины это делать — причина чисто культурная: «у нас тысячи програмистов, который не умеют ничего, кроме JS». И самое смешное здесь то, что серверный JS совершенно не похож на клиентский JS — о каком знакомстве может идти речь? Ладно были бы готовые решения под JS — но нет, готовых решений тоже не было. Но куда там, менеджменту и HR-ам ничего не пояснишь — «нам нужен эксперт с 3+ годами опыта на JS».

Не вижу в чём проблема. Ну кроме скорости выполнения, разумеется.

Давай спросим алису:

1. Обход цепочки прототипов: в отличие от Object.keys() (который рассматривает только сам объект), цикл for...in перебирает все перечисляемые свойства во всей цепочке прототипов. Для выполнения цикла движок должен обойти объект, затем его прототип, затем прототип прототипа, проверяя каждое свойство на перечисляемость.
2. Затенённые свойства: если свойство существует и в объекте, и в его прототипе, движок должен убедиться, что обращается только к «ближайшему» из них. Для этого во время цикла необходимо отслеживать набор имён свойств, которые уже были посещены, что существенно увеличивает нагрузку на память и процессор.
3. Преобразование в строку: даже при использовании с массивом цикл for...in обрабатывает индексы как строки (например, «0», «1»), а не как числа. Это вынуждает движок выполнять преобразования типов или отказываться от «быстрого» числового пути, который обычно используется для массивов.
4. Произвольный порядок: исторически спецификация не гарантировала определённый порядок перебора. Хотя современные движки стабилизировали этот порядок, сложность его поддержания при потенциальном изменении объекта во время цикла создаёт множество «особых случаев», которые мешают компиляторам Just-In-Time (JIT) делать быстрые предположения.
5. Деоптимизация (специфично для V8): во многих версиях движка V8 (Chrome/Node.js) функции, содержащие for...in, исторически помечались как «Не оптимизировать» или «Отключить Crankshaft». Поскольку форма объекта (Hidden Class) может меняться или быть непредсказуемой в цепочке прототипов, компилятор часто отказывался от попытки оптимизировать такую функцию в машинный код, возвращаясь к гораздо более медленному интерпретированию байт-кода.

Но, как я уже намекнул, современные линтеры просто выдают предупреждения при использовании for...in, у этой конструкции почти нет адекватных применений. И, тем не менее, это фундаментальная часть JS.

byko3y ★★★★
()
Ответ на: комментарий от firkax

ты сам переводил

Частично. Большие абзацы слегка исправлял после DeepL.

dataman ★★★★★
() автор топика
Ответ на: комментарий от byko3y

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

firkax ★★★★★
()
Ответ на: комментарий от byko3y

Давай спросим алису:

блин, какойже бред она генерит с умным видом о_0

JS так-то язык норм, для мелких поделий (не люблю питон),

но бесполезный for in это фигня, в современном пошел такой лютый оверэнжиниринг, это пц:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using

drsm ★★
()
Ответ на: комментарий от byko3y

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

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

bread
()
Ответ на: комментарий от drsm

но бесполезный for in это фигня, в современном пошел такой лютый оверэнжиниринг, это пц

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

byko3y ★★★★
()
Ответ на: комментарий от bread

Если бы другая скриптота предложила что-то сравнимое с нодой, возможно так бы js не распух.

Здрасти. OpenResty (Lua) ­прямой конкурент, и даже был быстрее ноды по состоянию на 2010 год. Erlang укатывал любую ноду по надёжности и масштабируемости вообще без шансов. Go вышел в том же году и точно так же имел минимум библиотек — даже в 2010 году под ноду было примерно ничего из готовых библиотек, примерно как и под Go.

Но объективно питон, руби и пхп это инвалиды, которые не тянули уже и 15 лет назад.

Забавно, что ты упоминаешь именно питон, руби, и пхп — языки с огромной инерцией сообщества и с отсутствующей архитектурой языка (дизайном, идеей — как хочешь называй). Я как бы не спорю, что подход «используем то, что чаще выдаётся в гугле» имеет очень серьёзные рациональную подоплёку в бизнесе (best practices, industry standards, etc), но если на минутку взглянуть на этот цирк с чисто технической стороны, то выглядит он забавно.

byko3y ★★★★
()
Ответ на: комментарий от bread

Что за поток бреда? Ты прям в каждом предложении написал чушь.

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

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

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от firkax

Пхп, наоборот, лучший из скриптовых языков, который я видел, и до сих пор им остаётся

Тиресное заявление. А можно пояснить эту позицию человеку, который с PHP мало знаком и обходит его стороной из-за низкой культуры разработчиков на нём?

Раст, который ты упомянул, вообще не скриптоязык, но писать сайты на нём, уверен, лучше чем на js. Тут правда у js есть небольшой плюс: его синтаксис более похож на си, чем растовый.

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

byko3y ★★★★
()
Ответ на: комментарий от byko3y

Тиресное заявление. А можно пояснить эту позицию человеку, который с PHP мало знаком и обходит его стороной из-за низкой культуры разработчиков на нём?

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

Ну и собственно пхп позволяет минимальными, по сравнению со всеми другими языками, затратами усилий, удовлетворить эти требования. Он не требует разбираться в каких-то сложных синтаксисах или парадигмах программирования (ООП в джава-стиле, внедряемое джавакаргокультистами, тут вредит, но пхп сам по себе его использовать не заставляет и тем более не заставляет делать его в джава-стиле), тебе не надо изучать всякие «шаблоны хелловордов», можно просто писать строка за строкой то что ты хочешь чтобы делала программа - как на шелле, но с синтаксисом, подходящим для написания алгоритмов. Ну ладно, в начале файла надо написать «<?php», но запомнить эти 5 символов несложно. Авторы пхп не стесняются (по крайней мере раньше не стеснялись) фиксить неудобные места языка локально - то есть, не впадая в идеализм на тему «надо чтобы всё было единообразно» - то есть каждый отдельный аспект языка удобен сам по себе, без теоретической оглядки на остальные его части. Так же он (хотя в последних версиях это подпортили) был крайне толерантен к разным ошибкам, что позволяет ещё ускорить скорость разработки и сократить время от начала работ до получения хоть как-то работающего продукта.

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

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

firkax ★★★★★
()
Ответ на: комментарий от byko3y

специальная олимпиада

люди работают (с) ))

отдельная дисциплина еще это WebReаlity там, сделали криво в major браузерах, так так и стандартизируем (iteration order по свойствам объектов как пример),

пхп это днище, достаточно взглянуть на стандартную библиотеку для работы с массивами. но туда тоже натащили уже, почти ява получилась :D

drsm ★★
()
Ответ на: комментарий от byko3y

И кодить я не умею — да и зачем, если нейросеть напишет лучше?

У нас в отделе работает промт-инженер, который примерно так и написал в резюме. Моё собеседование не прошёл, но чем-то впечатлил женщин-начальниц. Пришлось взять. Пока удаётся не подпускать к проектам, за которые отвечаю лично.

Kogrom
()
Ответ на: комментарий от Kogrom

Моё собеседование не прошёл, но чем-то впечатлил женщин-начальниц

Блин, ты заинтриговал... Ну типа если бы в моей команде такой был, то я бы точно разузнал, к чему мне готовиться. Я работал со скрытым пользователем ИИ, но он всё-таки был технически компетентен сам по себе.

byko3y ★★★★
()
Ответ на: комментарий от firkax

Это не первая экзотическая идея от тебя, насколько я помню. Тем не менее, мне нравится такая эксцентричность, от бест практисов меня уже давно воротит.

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

Инструмент с мощным быстрым прототипированием должен неизбежно быть сложным в освоении, потому что в нём будет много сочетаемых готовых блоков — все эти готовые блоки и средства их эффективной композиции нужно изучать. Яркий пример — APL. Он настолько дикий, что даже математики от него отказались.

Если говорить про твои задачи «быстро и эффективно что-то наговнякать», то здесь сразу можно вспомнить F# и Nim — все два оба из которых довольно малоизвестны. Вот пример на PHP:

<?php
$fh = fopen('users.csv', 'r');
$header = fgetcsv($fh); // skip header
while (($row = fgetcsv($fh)) !== false) {
    if ((int)$row[1] > 25) echo $row[0] . "\n";
}
fclose($fh);

он же на F#:

#r "nuget: FSharp.Data"
open FSharp.Data

type Users = CsvProvider<"users.csv">
let users = Users.Load("users.csv")
users.Rows |> Seq.filter (fun r -> r.Age > 25) |> Seq.iter (printfn "%s")
Сложнее?

Далее, PHP:

$html = file_get_contents('https://example.com');
preg_match('/<title>(.*?)<\/title>/i', $html, $matches);
echo $matches[1] ?? "No title\n";

Оно же на F#:

#r "nuget: FSharp.Data"
open FSharp.Data

let html = HtmlDocument.Load("https://example.com")
let title = html.Title() |> Option.defaultValue "No title"
printfn "%s" title

Я думаю, что после этих примеров будет очевидный вывод, который мне был очевиден потому, что я уже давно видел эти примеры — дело не в эффективности и быстром выпуске в продакшен, дело в необучаемости и ленивости кожанных мешков, которые не хотят изучать этот самый «мощный инструмент», а хотят херак-херак — и в продакшен.

И ИМЕННО ПОЭТОМУ LLM стали настолько популярными — они приносят тапочки прямо к кровати, «веб-сервис написан, что ещё желает мой господин» — а «господин» уже полубессознательный овощь, «господин» ещё больше вываливается из контура управления, и теперь малейшая ошибка приводит к «нейросеть, ну придумай что-нибудь, ну разберись, что там оно значит и почему не работает».

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

byko3y ★★★★
()
Ответ на: комментарий от anonymous_sama

Автор пишет, что его фрустрировали бугогашеньки спецификации JS. Как он может хорошо знать архитектуру ноды?

Shadow ★★★★★
()
Ответ на: комментарий от byko3y

Если у тебя строка неявно привелась к числу и из-за этого сломалось хрен знает что, хотя внешне код выглядит правильно — это уже «приходится разбираться с низким уровнем».

В большинстве случаев она приведётся и продолжит работать. Даже более того, ты можешь из своего примера ниже убрать (int) благодаря этому, или вообще не знать чем '25' отличается от 25. Или писать что-то, что оказывается в итоге '123;f;xyz;7' > '11qwe', подразумевая сравнение 123 и 11 но не тратя время на парсинг этих строк, и оно сработает. Правда, есть неожиданность: '123;f;xyz;7' > '11e4' вернёт false, но в целом пофиг. Впрочем, лично я вышеописанной логикой стараюсь не пользоваться.

Сложнее?

Да, сложнее. Если что F# я не знаю, расписываю как это выглядит со стороны.

open FSharp.Data

Что это, зачем? За какой аспект логики работы отвечает эта строчка? Ни за какой, но она требуется.

type Users = CsvProvider<«users.csv»>
let users = Users.Load(«users.csv»)

Зачем мы два раза пишем о том что хотим загрузить users.csv? Чем Users отличается от users?

users.Rows |> Seq.filter (fun r -> r.Age > 25) |> Seq.iter (printfn «%s»)

Откуда взялось какое-то seq? Тут куча лишних букв, которые зачем-то надо помнить и писать, намного нагляднее выглядело бы примерно так:

users.Rows |> filter (Age > 25) |> iter (printfn «%s»)

Кстати, насчёт этого seq. Вот есть «system.out.println» в джаве, а есть «echo» в пхп. Тут та же история с огромным преимуществом пхп, ты ему пишешь «печатай» - он печатает. В джаве же «напечатать строку в поток out который часть системного чего-то там». Зачем там уточнять про системность, зачем там уточнять что печатаем в «out»?

Касательно второго примера сказать ничего не могу, регулярки мне не нравятся.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.