Go сделали скучным — и после нейросетей это выстрелило
- пятница, 25 сентября 2026 г. в 00:00:14
Go двадцать лет дразнили языком для дураков. Без дженериков, без перегрузок, без нормальных перечислений, без магии. Синтаксис учится за выходные, выразительность — на уровне табуретки.
А теперь смотрим на 2026 год. Индустрия побежала писать AI-агентов и гейтвеи — и пишет их на Go или Rust. Нейронки генерируют код на Go лучше, чем почти на любом другом языке, потому что Go читается. Можно сказать, что язык оказался настолько тупым, чтобы снять пороги входа для LLM. И это, возможно, оказалось самой дальновидной ставкой в истории языков программирования.

Сегодня со мной Дмитрий Матрёничев — руководитель команды Open Source в MWS Cloud Platform (MTS Web Services). Пишет на Go примерно с 2012–2013 годов, с конкарренси в пределах одного процесса. До этого C++ и Java.
Так вот, главное, что нужно понять про Go: скуку в него положили намеренно. И положили её люди, у которых на это были очень личные причины.
Потому что это изначально был язык, чтобы качать Хром.
Итак, слово Дмитрию.
Это официальная версия рождения Go.
Это правда, но не вся. В 2007 году действующим стандартом C++ был C++03. Следующий загадочно назывался C++0x, потому что никто не знал, в каком году он выйдет, — а когда выйдет, было непонятно, как быстро его начнут поддерживать компиляторы. Каждый компилятор умел строго свой кусок стандарта, но не всё сразу, и кто писал на плюсах в то время, прекрасно помнит эту боль: ни умных статических анализаторов, ни продвинутых коллекций — всё это появится потом. Сам стандарт C++ — тысяча с лишним страниц А4, сейчас уже больше. Хорошая энциклопедическая книжка: ей можно драться. Думаю, и череп проломить можно.
Java выглядела живее, но со своим ООП головного мозга и сильным уклоном в энтерпрайзную бизнес-логику. Сетевая библиотека — так себе. Плюс огромный багаж энтерпрайз-экосистемы. Плюс Google, судя по всему, уже тогда волновался о потенциальном столкновении с Oracle и искал запасные варианты — как мы теперь знаем, волновался не зря. Сегодня, в 2026-м, эти аргументы кажутся несостоятельными, все более-менее доросли до одного уровня. А тогда — нет.
Python — скриптовый язык без проверки типов: прототипировать удобно, на этом всё. И отдельный пункт: хотелось юникод из коробки. Какой юникод в Питоне в 2009 году?
Ещё хотелось, чтобы конкарренси была отделена от многопоточности. Потоки и единицы выполнения — разные вещи, и язык должен это понимать.
А теперь самое смешное. Задача, под которую всё это затевалось, звучала предельно конкретно: нужна штука, которая позволит качать бинарники Хрома с dl.google.com. Под это сделали язык. Целый язык — чтобы раздавать файлы. По сути, он выполнял функцию перегонки байтиков, и, если открыть стандартную библиотеку Go, это прослеживается практически везде. Весь язык построен вокруг этого.
Звучит как чудовищный оверинжиниринг? С одной стороны, да. С другой — когда ты Гугл, оверинжиниринг перестаёт быть оверинжинирингом.
Но над языком и компилятором работали не только они. Иэн Лэнс Тейлор — на тот момент один из ведущих контрибуторов GCC. Расс Кокс — человек, который потом долгие годы был бессмертным техлидом гошного компилятора. Сетевой стек писал Брэд Фицпатрик — тот самый, который до этого сделал Живой Журнал и memcached. Вероятно, он знает, о чём речь, когда говорит про сеть.
Теперь главное про этих людей. Они все из computer science — теория типов, теория компиляторов, теория того, как делаются языки. В наших университетах обычно учат computer engineering; здесь другая лига. И с дженериками все они были прекрасно знакомы. В этом месте обычно ждут вывода «и поэтому они построили богатый, сложный язык». Вывод обратный: они знали теорию достаточно хорошо, чтобы не тащить её в язык целиком.
Каждая странность Go — шрам от конкретной боли конкретного ветерана. Когда такие люди делают «примитивный» язык — это осознанное решение, а на осознанные решения у них было полное право.
На старте в Go не было трёх вещей, которые принято требовать от приличного языка: перечислений, перегрузок и дженериков. Дженерики в итоге доехали — через десять лет после релиза. Первых двух нет до сих пор.
Вокруг этой задержки вырос популярный миф — будто авторы языка принципиально выступают против дженериков и вообще любых новых вещей. Есть версия ещё веселее: дженерики якобы не завозили, потому что не доверяли разработчикам — вдруг наломают дров.
Нет такого. Честный ответ авторов всегда звучал по-другому: не знали, как сделать.
Больше того, обобщённые типы жили в Go с первого дня, целых два: map, которая под капотом всё-таки дженерик не до конца, и slice — полноценный дженерик со своими append и make. Все прекрасно понимали, что разработчикам нужны такие типы. Вопрос был чисто инженерный: можно ли взять у кого-нибудь готовое хорошее решение и сделать по-нормальному?
Пошли смотреть. Java: затирание типов, боксинг везде, всё убегает в хип. В эту боль я в своё время влезал лично, когда беспокоился за обратную совместимость на Java: из-за затирания у коллекции в рантайме в принципе нельзя спросить, она интов или строк, и сложные конструкции выразить просто невозможно. От этого до сих пор страдает Scala и вообще все языки на JVM. Не понравилось. C++: разворачивание шаблонов на этапе компиляции, мономорфизация, компиляция длиной в жизнь. Не понравился C#, который все так любят приводить в пример: генерация кода прямо в рантайме — дженерик компилируется в исполняемый на твоём процессоре код при первом же вызове. Для сравнения, Java делает JIT то ли на сотне тысяч, то ли на миллионе вызовов. Тоже оказалось не то.
В Java аллокация почти бесплатная: есть молодое поколение и старое, любое размещение в хипе работает почти как помещение в стек, а сборщик потом перекладывает выжившее во второе поколение, которое сканируется относительно редко. У Go всё устроено по-другому: полноценный аллокатор, который ищет подходящий участок памяти и размещает объект. Дороже. Поэтому путь «пускай всё убегает в хип, переживём» для Go был заказан с самого начала.
Дизайны дженериков при этом не лежали в столе. Есть целая череда issues, начиная с первой половины 2010-х, и в каждом описано очередное видение: вот так дженерики могли бы выглядеть. Каждый раз всё упиралось в одно и то же: как сделать их до конца — чтобы они нормально смотрелись в языке и не приводили либо к бесконечно компилирующимся бинарникам, как в плюсах, либо к разрастанию хипа, как в Java. Плюс на старте у гошного проекта было выделено совсем немного людских ресурсов, и тратить их на «срочно завезти дженерики любой ценой» никто не собирался.
А пока дженериков не было, стояла на месте вся остальная продвинутая эволюция. Нормальные итераторы, продвинутые типы перечислений, которые все так любят ставить Go в укор, — всё это упирается в обобщённый код. Дело было никогда не в «особенностях языка» и не в упрямстве авторов — только в дженериках.
И вишенка, моя любимая. Когда дженерики наконец вышли, выяснилось, что с их помощью можно инстанцировать приватные типы — просто потому, что вот так дженерики взаимодействуют с остальной системой типов. Команда хотела эту дыру закрыть. Не успела — в том числе благодаря вашему покорному слуге, который сообщил, что часть кода уже успела на это поведение завязаться. Решение приняли в фирменном стиле Go: ну, завязались так завязались.
Go 1.0 вышел в марте 2012 года. С тех пор обратную совместимость не ломали ни разу, кроме одного случая.
Речь про Go 1.22 и семантику переменной цикла: раньше for i := range ... создавал одну переменную на весь цикл, и замыкания в горутинах ловили её последнее значение — баг настолько канонический, что висел в каждом списке Go gotchas. В 1.22 переменная стала создаваться на каждую итерацию. Формально это слом совместимости — старый код мог полагаться на старое поведение. Команда поступила в своём стиле: прогнала изменение по всему коду Google, убедилась, что ломаются почти исключительно баги, а не фичи, и привязала новое поведение к версии в go.mod — модуль с go 1.21 продолжает компилироваться по-старому. Даже единственный слом обратной совместимости в истории языка сделали обратно совместимым.
Чтобы вы поняли глубину этой упёртости, расскажу свою любимую историю про язык вообще. В стандартной библиотеке Go были приватные символы, к которым тем не менее можно было обратиться с помощью хаков. В какой-то момент эти лазейки решили прикрыть. Что сделала бы любая нормальная команда? Прикрыла бы и написала в ченджлоге: кто завязался на приватный API — сам виноват.
Команда Go вместо этого прошла огромным сканом по GitHub и посмотрела, кто из библиотек к каким приватным символам реально обращается. И, если у библиотеки оказывалось достаточно загрузок — или тысяча звёзд, порог я уже не помню, — для этого приватного символа специально оставляли доступ. А сверху вешали стену позора, hall of shame: комментарий прямо над переменной или функцией — эта штука по-прежнему доступна через хак, потому что вот такие библиотеки к ней обращаются, и убрать её мы физически не можем.
Они даже не сказали: вы завязались на приватный API, это лично ваша проблема. Они сказали: часть экосистемы выстроила себя вокруг приватного — и приватное внезапно для себя стало публичным.
В go.dev/src/runtime/map.go над функцией makemap висит комментарий: «makemap should be an internal detail, but widely used packages access it using linkname. Notable members of the hall of shame include: github.com/ugorji/go/codec. Do not remove or change the type signature». Главный документ всей операции — issue #67401 от Расса Кокса: там описано, как изменение внутренностей stdlib сломало goccy/go-json, от которого зависит в том числе Kubernetes, и почему «ситуация неустойчива: internals are internal for a reason». Начиная примерно с Go 1.23 новые linkname к чужим приватным символам запрещены, а все старые «нарушители» разрешены навсегда. Дошло до того, что попадание в hall of shame стало вопросом репутации: мейнтейнеры cloudwego/frugal пришли с отдельным issue и попросили вычеркнуть их из позорного комментария, потому что переписали JIT на рефлексию. Есть ещё пакет shame — автогенерированные биндинги ко всем приватным символам, которые Go вынужден поддерживать вечно, чтобы любой желающий мог «вступить в зал славы» одной строчкой импорта.
На практике это обещание означает простую вещь. Я беру с Гитхаба библиотеку, которую не обновляли восемь лет, и точно знаю: она скомпилируется последней версией Go. Таких гарантий мне не даёт практически ни один язык. Вообще. Все любят завязаться на фичи, которые потом мутируют. Первым, кто более-менее адекватно оформил похожую гарантию в пользовательском пространстве среднего программиста, стал Rust со своим «этот проект компилируется вот этой версией языка» — и это появилось сильно позже, во второй половине десятых.
Связала ли такая политика руки? Связала. Но через версии языка в go.mod и через модернайзеры язык потихоньку доводят до современного состояния — а человек, который двенадцать-тринадцать лет назад написал код под go 1.0, сегодня просто компилируется и никому ничего не должен. По-моему, это скорее большой плюс, чем минус.
Ты не можешь один раз написать «теперь у нас есть дженерики» и пойти к следующей задаче. Так не работает: дальше ты бесконечно занимаешься улучшением генерируемого кода, эргономикой, тем, как новые типы взаимодействуют со старыми, ищешь ошибки на стыках. А выкинуть ничего нельзя — обещали же.
Поэтому внутри стандартной библиотеки потихоньку растёт кладбище.
Есть потрясающий интерфейс sort.Interface — он служит для сортировки произвольных типов. Вспомните, когда вы им пользовались последний раз. Я — на собеседовании, лет пять назад. Причём уже тогда можно было взять sort.Slice и просто передать функцию с индексами. Всё. Технически sort.Interface существует, код с ним компилируется — практически он уже на полпути в забытьё.
Есть container/heap — куча как структура данных из стандартной библиотеки, для которой нужно реализовать специальный интерфейс. Я именно про структуру из stdlib, а не про хип, куда убегают аллокации. Кажется, рядом лежит ещё и ring — честно говоря, уже не помню.
А ещё в Go есть комплексные числа. Встроенные прямо в язык, с первой версии. Зачем они там, кому нужны — неизвестно. Выкинуть — нельзя. Ведущий подкаста, у которого я недавно был в гостях, признался, что узнал об их существовании полтора года назад. Вот настолько они востребованы.
И при всём этом Go 2 не будет. Никогда. Это официальная позиция команды: язык будет потихоньку эволюционировать, старые вещи будут уходить в небытие естественным путём — ими просто перестают пользоваться. Никакого большого взрыва, никакой новой эры. Скучно? Скучно. В этом и смысл.
Может. Но зачем? Весь вопрос в существующей экосистеме.
Экосистема любого крупного языка давно сбалансировалась. Go занял прочную облачную нишу: всё, что связано с транзитом байтиков, всё, что стоит после балансировщиков, но до сложной обвесистой бизнес-логики. Это наша область, и, по сути, она не изменилась с 2012 года: рекламу крутим, трафик гоняем, виртуалочки поднимаем. Обработка байтиков — немного более продвинутая, чем тогда, но всё равно обработка байтиков.
Дрейф при этом происходит. WSO2 — контора, которая сидит на рынке с начала нулевых и двадцать лет строила всё на Java, — опубликовала большую статью о том, что в долгосрочной перспективе отказывается от Java в пользу Go.
Знаю и ребят, которые активно тащат Go в десктоп. Ну, удачи. Десктопная разработка — дело очень сложное и, самое главное, неблагодарное. За всю историю полноценный кроссплатформенный фреймворк для графических приложений более-менее получился один — Qt. И он родом из эры САПРов: интерфейсы из тысячи компонентов, на которые смотришь, как на панель приборов боинга — не нового, а того самого, шестидесятых-восьмидесятых годов, у которого панелей больше, чем людей в салоне.
Электрон тем временем съедает гигабайт памяти на приложение, которое делает буквально две функции. Я смотрю в счётчик памяти и согласен: пусть переписывают. Wails идёт по пути конкуренции с Электроном и действительно экономнее; Tauri на Rust ещё экономнее. Но у Go сложная история с линковкой и сишными библиотеками, а любой графический интерфейс — это всё равно нативные виджеты.
И главное — экономика. Как только встаёт вопрос трудозатрат, сразу всплывает вопрос платформ. Большинство оптимизаций в наше время делается ради мобильных: когда Электрон сожрал всю процессорную мощность и всю батарею телефона, люди идут и переписывают поделку на JS-фреймворке в настоящее приложение. Десктоп для всех — остаточное явление. Под винду берёшь C# с WinForms и рисуешь интерфейс в визуальном редакторе. Под макось — Swift, и Xcode позволяет всё задизайнить из коробки. А на линуксе люди из терминала не выбираются — кого интересует их мнение и какой GUI им вообще нужен? Графические интерфейсы разрабатывают для платёжеспособной аудитории.
Если же кому-то всерьёз не хватает возможностей поверх Хрома — это уже крайний случай, действительно сложное приложение. Туда честно берут Qt с плюсами, потому что у Qt есть коммерческая поддержка. Это важно: любая библиотека рано или поздно развалится, и хорошо бы в этот момент знать, что ты не единственный, кто столкнулся с проблемой. И что в интернете даже нейронка найдёт тебе ответ — вместо того чтобы выдумать функцию, которой не существует, и по ходу дела объяснять, что не прав ты, хотя компилятор говорит ровно обратное.
Есть, правда, одна оговорка. Память сейчас стоит, как космический корабль — спасибо буму нейросетей. Думаю, это временное явление. Но об оптимизированном коде индустрия снова начала задумываться, впервые за долгое время. Для компилируемых языков это шанс.
У каждого языка в индустрии своя ассоциация: говоришь «энтерпрайз» — слышишь Java, говоришь «машинлернинг» — слышишь Python, говоришь Go — слышишь cloud native. Обучение моделей за Питоном и останется, ничего лучше для этого в обозримое время не появится. А вот с инференсом картинка другая. Если сегодня кто-то пишет AI-агентов и гейтвеи, он пишет их на Go либо на Rust.
У го-агента простая магия: он легко собирается, и он нативный для платформы. Никакого байткода, никаких рантаймов — обычный исполняемый код. Плюс несложная экосистема, которая уже устоялась, настаканилась. Плюс низкий порог входа — в том числе для самой нейронки, которая может такого агента накидать сама. Хочешь — берёшь готовые агенты и гейтвеи, хочешь — пишешь свои, RAG'и собираешь.
Читаемость, за которую Go обзывали примитивным, работает в обе стороны. LLM хорошо генерирует код на Go, потому что Go легко читается. Но по той же причине код, написанный нейронкой без внешнего взгляда, виден на Go с ходу. Я недавно собеседовал несколько человек и понимал мгновенно, где код написан нейросетью. Спрашиваешь: а почему здесь сделано вот так? И человек отвечает какую-то чушь. Хотя нормальный ответ существует: слушай, ну здесь я просто воспользовался нейронкой, не стал запариваться. Честно — и вопросов нет. А когда человек выдаёт чужой код за свой и не может объяснить ни строчки, сразу понятно: ответственность за этот код он нести не готов.
Буквально на днях в репозитории появился пулреквест, у которого в соавторах гитового коммита значился Claude Opus 4.5 — не свежий 4.6, а именно 4.5, я проверял. И все разом задумались: а что вообще значит «соавтор»? Мы же не указываем в соавторах коммита Go или VS Code. Юристы, в том числе вполне подкованные в IT, параллельно спорят о смежном: модель, натренированная на горе чужого кода, — она свой код выдаёт или копирует ответы со StackOverflow, переименовывая переменные? У части коммерческих моделей не просто так стоит фильтр на выхлоп — чтобы наружу не вылетали запатентованные решения и куски под коммерческими лицензиями.
Я слышал даже гипотезу, что всё это соавторство — PR-акция: мол, Anthropic договорился с GitHub о дешёвой интеграции своих моделей в Copilot, а взамен Claude начали указывать в коммитах. По-моему, это какая-то шиза. Мы всерьёз обсуждаем авторские права инструмента — неодушевлённого объекта. Кто-то может владеть столом, но сам стол правами не обладает: ни авторскими, ни какими-либо ещё. Если идти этим путём, почему соавтором не указать верную супругу, которая приносила кофе и наставляла на правильные мысли — «смотри, у тебя вот здесь ошибка»? Писатели вон благодарят в конце книги любимую кошечку. Давайте прикладывать к каждому коммиту по две страницы о том, кто нас поддерживал и кто навёл на мысли.
Хорошая аналогия — заказ картины у художника. Ты детализируешь всё, что должно быть на холсте, буквально до того, где что стоит, несколько раз итерируешься по мелочам — и в итоге получаешь картину. Кто её автор? Нарисовал её художник. Только художник одушевлённый и за свою работу отвечает. У нейронки нет ни того, ни другого.
Сам вопрос в том обсуждении стоял предельно практично: принимает ли Go пулреквесты, сделанные с помощью ИИ? И мне очень понравился ответ Расса Кокса — того самого бессмертного техлида гошного компилятора. Три страницы А4, академично разжёваны все вопросы принятия кода, а суть простая. ИИ — очередной инструмент. Важно не то, какими инструментами написан код. Важно, чтобы человек, который этот код контрибутит, понимал, что код делает, был готов отвечать на вопросы по нему и мог сказать: да, я старался писать его как можно надёжнее и качественнее. Автором указывается тот, кто отвечает за результат и владеет им. Модель этим критериям не отвечает и отвечать не может. Сказать «Copilot написал — Copilot и владелец» не выйдет: владельцем кода тогда становится кто, Microsoft? Представляю себе реакцию Google.
Письмо, о котором идёт речь, лежит в треде «What should we do with CLs generated by AI?» в golang-dev, февраль 2026. Ключевая мысль в оригинале: «A powerful tool, a new kind of tool, but still a tool. At a high level, what we expect from contributors, reviewers, or maintainers is not changing» — какие бы инструменты ни выбирал контрибутор, ожидания и ответственность остаются прежними. Для сравнения, LLVM разрешает AI-инструменты при условии, что контрибутор имеет право лицензировать код под лицензией проекта, и прямо пишет: contributors are considered responsible for their contributions. Fedora требует относиться к сгенерированному коду как к черновику, ревьюить и тестировать всё перед отправкой, а заодно маркировать вклад трейлером вида Assisted-by.
И вот свежие данные Go Developer Survey 2025 (опубликован в январе 2026). 53% разработчиков пользуются AI-инструментами ежедневно, но есть и большая группа в 29%, которая не пользуется ими вовсе или открывала пару раз за месяц. Агентные режимы пока в зачаточном состоянии — только 17% назвали их основным способом работы, ещё 40% пробуют время от времени. Удовлетворённость AI-инструментами — 55%, причём с сильным перекосом в «скорее доволен» (42%) против «очень доволен» (13%), тогда как сам Go стабильно держит 90%+ удовлетворённости год за годом. То есть разработчики охотно берут нейронку — и при этом не торопятся ей доверять.
Теперь про похороны, они у нас раз в 15 лет. Сейчас модно спрашивать: а какая вообще разница, Go у тебя или Ruby, если и тот и другой код всё равно будет писать нейронка? Мантру «язык неважен» я слышу с 2005 года. Как и её родную сестру — «программисты больше не нужны». У этой второй каждый раз новое лицо. Сначала это был Rational Rose: рисуешь диаграммы, описываешь связи и последовательности взаимодействий — и получаешь более-менее готовый каркас кода, который чуть-чуть докодить, и приложение готово. Профессия изжила себя, расходимся. Потом наступил JavaScript: V8 встроили в ноду, и все вокруг объясняли, что теперь всё будет написано исключительно на JS, а остальные языки можно смело выкидывать на помойку. Теперь вот нейросети будут писать код за нас. А недавно мне попался обзор журнала аж 1991 года: появилось ООП, появились высокоуровневые языки — теперь программировать могут даже дети, программисты не так уж и нужны.

Нас реально хоронят каждые пятнадцать лет. Стабильный процесс. И мы, поверьте, даже не против.
Тем более что у гошников есть и отдельная страховка. Нейросетками хочется писать интернет-магазинчики: код там не генерирует денег вообще никак, поэтому на нём экономят как могут — максимум автоматизации, минимум живых людей. В сложных проектах с обвесистой бизнес-логикой своя история: там значение имеет даже не код, а формальные требования к нему. А у нас перегонка байтиков — та самая, с 2012 года. И вот её LLM как раз никто не доверяет. Представляете, какую рекламу нейронка через какое-то время начнёт крутить, если пустить её к нашим системам? Явно не ту, которую заказывали рекламодатели.
Мы ведь на самом деле просто эволюционируем. Программировали на перфокартах. Потом на ассемблере. Потом появился C, потом Java, параллельно Python. Потом языки начали таскать из функциональных веток то, что там реально хорошо себя проявило: иммутабельность, типы-перечисления, функциональщину всякую. Каждый виток кто-нибудь объявлял концом профессии — а это был очередной инструмент.
Я в этой эволюции сторонник одной простой вещи. Мы стараемся делать то, что: а) могут читать другие люди, б) отвечает требованиям заказчика и в) не жжёт все вычислительные мощности во Вселенной. Нейросети пока не вписываются минимум в два пункта из трёх.
А Go под эти три пункта затачивали с самого начала — задолго до того, как они стали модными.
P.S. Ссылка на наш телеграм-канал с обновлениями и прочими ништяками.