Про статью «Stop Making TUIs» и почему я с ней не согласен

Привет! Меня зовут Арсен Абдусаламов, я тимлид Авито Авто. Недавно я наткнулся на статью Томаса Птачека Stop Making TUIs, и она меня зацепила настолько, что захотелось развернуть дискуссию.
Автор рассказывает, что недавно увлёкся созданием нативных интерфейсов для macOS: написал просмотрщик Markdown (MDV.app), фронтенд для SageMath, музыкальный плеер, вики-помощника, трекер макронутриентов и пульт для Apple TV. Причём код всех этих интерфейсов был практически полностью сгенерирован AI-агентами — сам он почти не участвовал в их написании. «Создание хорошего UI — это очень тяжёлая работа: подобный код утомителен, однообразен, требует высокой точности и глубокого знания специфики платформы. Требуются годы, чтобы научиться хорошо выполнять такую работу. Именно поэтому я бы никогда не стал писать эту программу вручную», — так Птачек описывает свой опыт создания приложения MDV.app.
Далее автор проводит разграничение между CLI — интерфейсами командной строки и TUI — терминальными пользовательскими интерфейсами. Он пишет, что и первое, и второе является наследием 1970-х годов и ограничений телетайпов, но если CLI имеют незаменимые сценарии использования и их создание почти всегда оправдано, то TUI, по его мнению — это устаревшие, враждебные и ограниченные интерфейсы, которые исторически существуют только по двум причинам: из-за модемов и из-за нежелания Unix-разработчиков изучать такие фреймворки, как Motif.
Автор считает, что сегодня, когда AI-агент может написать нативный GUI за вечер, создавать новые TUI попросту незачем. «Даже с хорошим фреймворком, таким как Ratatui, Textual или Bubbletea, вы боретесь с терминалом, чтобы приблизиться к тому, что каждый нативный фреймворк предоставляет из коробки, — говорится в статье. — Вы можете потратить время и реализовать вполне рабочие версии множества стандартных TUI-виджетов: выбор даты, поле для ввода пароля, индикатор выполнения, текстовый редактор. Но большинство из них будет уступать системным аналогам, а их интеграция в общий макет потребует дополнительных усилий».
Похоже, автор только что обнаружил, что GUI не кусается. Радость понятная. Беда в том, что от «я научился» он тут же прыгает к «а вы все зря мучились»: мол, кто десятилетиями терпел ASCII и гордился этим как настоящий сисадмин — тот сам себе враг. Берите свой голый CLI, заворачивайте в нативное приложение — и разработка откроется вам с другой стороны. И вот здесь, на мой взгляд, стоит притормозить и разобраться в деталях.

Проблема в постановке вопроса
Основная логическая ловушка статьи заключается в том, что TUI и GUI сравниваются как две равноправные и взаимоисключающие альтернативы. Но в современной разработке хороший терминальный инструмент не делает такого выбора.
Правильно спроектированный инструмент следует философии CLI-first: у него есть базовый интерфейс командной строки (CLI) с флагами и выводом в stdout, а интерактивный TUI-режим — это лишь опциональная надстройка поверх него. Не суть, а дополнение.
Рассмотрим два практических примера из экосистемы Charm:
Glow. Если запустить
glowбез аргументов, откроется интерактивный TUI с навигацией по файлам. Но если вызватьglow README.md, программа просто отрендерит содержимое вstdout. Хотите почитать с комфортом? Используйте интерактивный режим. Хотите засунуть результат в пайплайн (| grep ...)? Пожалуйста, это работает из коробки.Crush (AI-агент от Charm). В интерактивном режиме это полноценный TUI со стримингом, поддержкой LSP и MCP. Но команда
crush run "поправь тесты в pkg/auth"запускает того же самого агента в безынтерфейсном режиме, направляя результат вstdout. Такой вызов можно без боли интегрировать в CI/CD-пайплайн или git hook.
Ровно этого у нативного GUI нет и быть не может по определению. Кнопку в окне SwiftUI из cron не нажмёшь. Вывод из одного графического приложения в другое через стандартный pipe (|) не прокинешь. Терминальный инструмент умеет это, потому что его интерактивный режим — это всего лишь тонкая, удобная обёртка над командой, которая в автоматизированном сценарии в человеке не нуждается.

А как же GUI с CLI-режимом?
Здесь я предвижу возражение: «Постойте, многие GUI-приложения тоже умеют работать в CLI-режиме! Тот же Google Chrome с флагом --headless, VS Code с командой code --wait, или масса других Electron-приложений, принимающих аргументы командной строки».
Это действительно так, и важно это признать, чтобы картина была объективной. Однако между «GUI, у которого есть CLI-флаги» и «CLI-инструментом с TUI-обёрткой» есть фундаментальная архитектурная разница:
В GUI-first приложениях CLI-режим часто является вторичной надстройкой или компромиссом. Даже в headless-режиме такие приложения могут тянуть за собой тяжелые зависимости, требовать эмуляции дисплея (например, Xvfb) или специфичных сред выполнения. Их вывод в консоль зачастую не предназначен для парсинга, а попытка использовать их в compose-пайплайнах (
app1 | app2) выглядит как архитектурный костыль.В CLI-first инструментах голая консольная версия является первичной и основной. TUI здесь спроектирован так, чтобы легко сниматься, оставляя после себя чистый, компонуемый (composable) поток данных (
stdin/stdout). Это не эмуляция работы скрипта, а его естественная среда обитания. Один и тот же бинарный файл одинаково хорошо служит и человеку, и скрипту.
С чем я согласен
Единственный тезис, с которым я полностью согласен: TUI без неинтерактивного (CLI) режима — это действительно бессмысленная вещь.
Если инструмент заставляет вас взаимодействовать с ним только через псевдографику, не давая альтернативы в виде флагов и stdout, то это просто GUI, сделанный из текста. Он наследует все ограничения терминала (сложности со скроллом, выделением, доступностью), но не имеет ни одного из его преимуществ (компонуемости, лёгкости автоматизации).
Однако лечить эту проблему переездом на SwiftUI или другой нативный фреймворк — это как лечить отсутствие флагов у утилиты покупкой нового ноутбука. Решение лежит не в смене парадигмы на графическую, а в соблюдении базовой гигиены разработки CLI-инструментов: сначала сделай работающую команду с флагами, а потом (если нужно) оберни её в Bubble Tea или другой фреймворк для интерактивности.
CLI с флагами и stdout — это база. Интерактивный режим поверх него — опция для удобства человека. Инструменты вроде Glow и Crush устроены именно так, предоставляя один бинарный файл для решения обеих задач.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.