Меня зовут Стас. Коммерческой разработки — ноль. Ни фронт, ни бэк. Есть MacBook Air M2 на 16 гб и привычка докапываться до сути.

Приложу GitHub-репо -> HtrBox. Все продовые креды перероллены, так что все ок. Да, надо Github Secrets. Но мне было лень.

В декабре 25 года, когда поехал в гости к родителям праздновать Новый Год, столкнулся с небывалой для меня критической проблемой - отсутствием доступа к интернету (ChatGPT, Grok, Claude, YouTube, Telegram, и так далее). Мой родной регион был одним из первых, на ком РКН тестировал белые списки. К тому же до конца 25 года я пользовался для обхода блокировок OpenVPN. Энтузиасты выкладывали в открытый доступ ключи. Да, с моей стороны это было пренебрежением безопастности. Однако меня это вполне устраивало и я был доволен, пока он не был забанен.

Это статья о том, как из этой точки я пришёл к рабочему SaaS: бэк на FastAPI, фронт на React, PostgreSQL, деплой на несколько VPS и AI-assisted процесс разработки, который я выстраивал по ходу дела.


Hysteria2: с чего всё началось

Я не готов мириться с принудительными блокировками интернета, для меня важны и удобны инструменты и сервисы которые не работают без VPN. Поэтому находясь все еще в регионе в поисках решения я наткнулся на него - протокол Hysteria2. После чего быстренько развернул VPS, настроил его и был рад возвращению сия интернета.

Однако во время чтения доки меня зацепили возможности этого протокола. По сути протокол, удобно упакованный в докер, давал отличный и удобный API.

И вот я начал дергать за ручки сначала курлами. Потом пришла мысль автоматизировать через Python. Так познакомился с библиотекой requests. Накидал скрипт с хардкоженным словарём пользователей и функцией опроса /online. Код был простым — но факт того, что я могу программно управлять VPN-сервером - восторг. Захотелось сделать из этого что-то стоящее.

Примерно в это же время наткнулся на видео Как устроена оплата картой — оно показало мне возможности REST API и как из маленьких кусочков собирается что-то большое. После этого я понял - надо делать!


Архитектура: три независимых контейнера

С самого начала разделил проект на три изолированные части:

htrBox/
├── backend/      — FastAPI, PostgreSQL, бизнес-логика
├── frontend/     — React + Vite, пользовательский интерфейс
├── hysteria/     — Hysteria2, принимает VPN-соединения
└── docker-compose.yaml

Каждая часть — отдельный Docker-контейнер. Зоны ответственности не пересекаются: бэк общается с Hysteria2 по REST, фронт общается с бэком по REST, Hysteria2 ни о чём кроме VPN не знает.

docker-compose.yaml поднимает весь стек одной командой. Hysteria2 тянется с Docker Hub как готовый образ. Бэк и фронт собираются локально из Dockerfile.

Навайбкдив какую то часть backend'a - надо правильно написать Dockerfile - инструкцию по сборке образа, причем таким образом чтобы часто перестраиваемые слои были ниже в инстуркции. Это важно для оптимизации сборки образа. 

Для продакшна рядом лежит prod/infra/docker-compose.yaml с кончиком под каждую VPS-ноду:

prod/servers/
├── docker-compose.ge.yaml   — Германия
├── docker-compose.nl.yaml   — Нидерланды
├── docker-compose.pl.yaml   — Польша
└── docker-compose.se.yaml   — Швеция

Hysteria2-ноды принимают соединения пользователей и проксируют трафик. Бэк живёт в Yandex Cloud отдельно.


Бэкенд: FastAPI + PostgreSQL

Структура выстроилась как то так:

backend/source/
├── main.py
├── auth_jwt.py
├── config.py
├── database.py
├── schemas.py
├── rate_limiter.py
├── maintenance.py
├── traffic_collector.py
└── routers/
    ├── auth.py
    ├── users.py
    ├── hysteria.py
    ├── servers.py
    ├── traffic.py
    └── ws.py

JWT и httpOnly cookie

Авторизация — JWT с двумя токенами: access и refresh. Refresh-токен хранится в httpOnly cookie — проставляется через заголовок в ответе бэка. Это не случайное решение: httpOnly значит что JS на странице не видит этот куки. XSS атака не украдет токен.

Потом схема стала взрослее. Refresh-токен в базе хранится не как исходная строка, а как SHA-256 хеш. Сам токен живёт только в cookie у клиента, в базе — только отпечаток, по которому можно проверить и отозвать сессию. Logout — это удаление конкретного refresh-токена из таблицы. Блокировка пользователя — удаление всех его refresh-токенов.

def create_refresh_token(username: str, conn) -> str:
    token_value = secrets.token_urlsafe(48)
    token_hash  = _hash_token(token_value)
    expires_at  = datetime.now(timezone.utc) + timedelta(days=JWT_REFRESH_TTL_DAYS)
    with conn.cursor() as cur:
        cur.execute(
            "INSERT INTO refresh_tokens (token, username, expires_at) VALUES (%s, %s, %s)",
            (token_hash, username, expires_at),
        )

    return token_value

Ещё одно решение: роль пользователя не лежит в JWT. В access-токене только username, срок жизни, тип токена и jti. Роль читается из базы на защищённых запросах. Это добавляет один запрос к БД, зато если я перевожу пользователя из admin в user или блокирую его — изменение применяется сразу, а не через 30 минут когда протухнет токен.

В структуре есть routers/ws.py и useWebSocket.ts на фронте — WebSocket-инфра написана, но код закомменчен. На текущем масштабе поллинг через TanStack Query справляется, усложнять без необходимости не хотелось. Задел на будущее.

База данных

У меня совершенно не было опыта работы с базами даннныз на тот момент. Что такое foreign key или primary key — загадка. Поэтому скачал DataGrip, сделал дамп базы с прода и начал разбираться. PostgreSQL уже крутился на проде, но что происходило внутри — было непонятно. Для понимания баз данных сделал локальную среду для лабораторных работ по SQL на базе PostgreSQL 17 в Docker-контейнере

Нашёл классный курс от Postgres Pro и их же книгу по SQL, где объясняются ключевые концепции: как проектировать схему, что такое нормализация, как писать запросы, как делать DDL. После этого база данных стала для меня интересным объектом исследования. К тому же пришло понимание, что ИИшка не все связи между таблицами выстроила, поэтому могли быть висячие данные (которые должны удаляться на ON DELETE CASCADE).

Как тестил API

Для тестирования API использую Postman. Классная фича — наследование авторизации коллекции: можно задать токен один раз на уровне коллекции, все запросы его подхватывают. Три часа потратил на то что Postman не видел печенки — оказалось, нужно выставить тумблер Cookie Jar в OFF для каждого эндпоинта отдельно.

postman mood
postman mood

Отдельный неприятный момент — пароль VPN-клиента. В таблице users два разных секрета:

password      — пароль от аккаунта в веб-приложении
hyPassword    — пароль, с которым Hysteria2 пускает пользователя в VPN

password хранится нормально — bcrypt-хешем. А hyPassword хранится в открытом виде. На первый взгляд звучит как ошибка новичка.

Но это ограничение модели Hysteria. Пользователь получает ссылку вида:

hysteria2://username:hyPassword@server:443#label

Клиенту нужен пароль — он вшит в connection URL. Серверу тоже: Hysteria вызывает auth callback и передаёт username/password, а мой бэк должен сравнить их с тем, что выдал пользователю. С bcrypt так не выйдет: из хеша нельзя восстановить строку для генерации URL.

Фрагмент генерации ссылки в routers/hysteria.py:

url = (
    f"hysteria2://{username}:{row['hyPassword']}"
    f"@{host}:{srv['port']}"
    + ("?insecure=1" if insecure else "")
    + f"#{srv['label'].strip()}"
)

Компромисс такой: пароль аккаунта и пароль VPN — разные сущности. hyPassword генерируется случайно, пользователь его не придумывает, его можно регенерировать — и старая ссылка сразу становится бесполезной. Доступ к базе при этом должен быть жёстко ограничен, потому что это по сути секретное хранилище.


Сбор трафика: сложнее, чем казалось

Самая неожиданно интересная часть бэка — не логин и не CRUD пользователей, а сбор трафика.

Я думал, Hysteria отдаёт что-то вроде «пользователь alice потратил 120 МБ за последние пять минут». Было бы удобно: взял, записал, показал график.

На деле Hysteria отдаёт кумулятивные счётчики байт с момента старта процесса. То есть не «сколько потрачено за интервал», а «сколько всего насчитано с момента запуска». Если просто писать это значение в базу — график будет бессмысленным.

Пришлось сделать маленький traffic collector:

  1. Раз в N секунд получить список активных серверов из БД.

  2. Для каждого сервера сходить в его Hysteria management API: GET /traffic.

  3. Нормализовать ответ, потому что разные версии Hysteria возвращают немного разную форму JSON.

  4. Для каждой пары username + server_id взять прошлое значение из traffic_last.

  5. Посчитать delta = current_total - last_total.

  6. Положить дельту в traffic_5m, округлив время вниз до границы 5-минутного бакета.

  7. Прибавить дельту к users.usedTraffic.

  8. Обновить traffic_last.

Получились две таблицы с разной ролью:

traffic_last   — техническая память коллектора, последнее увиденное значение
traffic_5m     — временной ряд для графиков и аналитики

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

Решение: если current_total меньше last_total — считаю что счётчик сбросился и беру current_total как новую дельту. Часть трафика между последним poll и рестартом можно потерять, но lifetime-счётчик не портится.

if total_gb >= last_total_gb:
    delta_gb = total_gb - last_total_gb
else:
    logger.debug(
        "Counter reset for user %r on server %r (%.6f -> %.6f GB)",
        username, server_id, last_total_gb, total_gb,
    )
    delta_gb = total_gb

Добавил защиту от странных скачков: если за один poll прилетает больше 5 GB — считаю аномалией и не записываю. Это спасает от поломанного ответа API или бага, который нарисовал бы пользователю фантастический расход.

Все HTTP-запросы к Hysteria делаю до открытия транзакции в Postgres. Сначала собрал данные с нод, потом открыл соединение с базой, записал всё одной транзакцией и закрыл.


Rate limiting: просто и без Redis

Для публичных эндпоинтов добавил rate limiter. Не промышленный, не распределённый, без Redis — обычный in-memory словарь с фоновым cleanup-потоком.

Решает две задачи: замедляет перебор паролей на /auth/login и ограничивает частоту запросов на публичных эндпоинтах вроде регистрации и генерации connection URL.

Есть важный нюанс. Для обычного логина можно лимитировать по IP. А вот Hysteria auth callback приходит не от пользователя напрямую, а от VPN-сервера. Если лимитировать этот эндпоинт по IP — один проблемный пользователь может заблокировать авторизацию всем, потому что все запросы придут с одного адреса ноды. Поэтому для Hysteria auth ключ лимита — username, а не IP.

Ограничения очевидны: состояние сбрасывается при рестарте бэка, не шарится между несколькими репликами, не спасает от атак с тысяч IP. Но на текущем масштабе это осознанный компромисс — Redis ради первой версии был бы overkill.


AI-assisted разработка: рабочий процесс

Просто «писать с ИИ» и «иметь воспроизводимый процесс с ИИ» — разные вещи. Хочу остановиться на этом подробнее.

Проблема контекста

ИИ не удерживает контекст между сессиями. Каждый раз объяснять структуру проекта, эндпоинты, .env, связи между частями — боль и трата токенов. Решение: скармливать архив проекта напрямую в чат. Claude и Manus умеют работать с tar-архивами.

pack.sh: упаковка контекста

Написал bash-скрипт с тремя режимами:

pack          # весь проект (кроме .git, venv, node_modules, dist)
pack back     # только backend + docker-compose.yaml + .env
pack front    # только frontend + docker-compose.yaml + .env
pack --dry-run [all|back|front]  # показать команду без выполнения

Три режима нужны для экономии токенов: если правлю роутер на бэке — фронт с его 50+ компонентами не нужен в контексте. Режим --dry-run — перед тем как скинуть архив в чат, убеждаюсь что в него не попало лишнее. Флаг -yубирает подтверждение перезаписи, чтобы не прерывать автоматизацию.

Исключения при полной упаковке:

declare -a EXCLUDES=(
    ".git"
    ".env"
    "backend/venv"
    "frontend/node_modules"
    "frontend/package-lock.json"
    "frontend/dist"
    ".DS_Store"
)

TODO-подход к разработке фич

Раньше сразу говорил ИИ: «давай реализуем фичу». Теперь — нет. Сначала:

Не трогай код. Опиши план внедрения фичи с TODO-маркерами и подробным описанием каждого шага — так, чтобы человек, который вообще не в теме проекта, понял что делать.

Обкашливаем план. Только потом: «вперёд, с первого пункта».

ИИ отдаёт только изменённые файлы, включая обновлённый TODO.md с прогрессом после каждого шага. Смотрю git diff в VSCode — если всё ок, двигаемся дальше.

Кайф такого подхода: когда ИИ галлюцинирует или заканчиваются токены — не страшно. Делаю ./pack.sh, перекидываю архив с актуальным TODO.md на новый аккаунт и продолжаю с того места где остановился. Полный контроль над процессом.

Мне 15 Google аккаунтов позволили не потратить ни единого цента на ИИ.


Дизайн-система для ИИ

Это самое нетривиальное решение во всём проекте. И возникло из конкретной боли.

Когда просишь ИИ написать новую страницу или компонент — он расставляет Tailwind-классы случайно. На одной странице text-sm, на другой text-xs, на третьей text-base. Где-то gap-2, где-то gap-3. Цвета, отступы, радиусы — всё вразнобой. Проект становится грязным.

Решение: создать один файл с правилами, которому ИИ следует при каждом запросе. PROMT.md во фронтенде — это 250+ строк инструкций, которые описывают дизайн-систему адаптированную под ИИ.

Структура системы:

src/styles/
├── tokens.ts        — атомарные токены: typography, surface, radius, spacing
├── animations.ts    — transition, hover, press, enter, loading
├── variants.ts      — colorScheme + тип ColorScheme
├── index.ts         — единая точка входа, реэкспортирует всё
└── cStyles/         — стили компонентов, разбитые по папкам
    ├── uiStls.ts
    ├── commonStls.ts
    ├── layoutStls.ts
    ├── dashboardStls.ts
    ├── usersStls.ts
    ├── serversStls.ts
    └── pagesStls.ts

Правило маппинга жёсткое: компонент из components/ui/ — стили только в cStyles/uiStls.ts. Компонент из pages/ — только в cStyles/pagesStls.ts. ИИ не может положить стили куда попало.

Использование в JSX после миграции:

import { styles, colorScheme } from "@/styles";
import type { ColorScheme } from "@/styles";
const s = styles.userRow;
const v = colorScheme[variant];
пример определения типографики дизайн-системы
пример определения типографики дизайн-системы

Ноль сырых Tailwind-классов в JSX. Все цвета — через colorScheme, все отступы и типографика — через токены. Когда прошу ИИ написать новый компонент — он читает PROMT.md и работает в системе координат дизайн-системы, а не изобретает классы заново.

Цветовые варианты — через ColorScheme, а не через локальный variantStyles в компоненте:

// так нельзя — локальный вариант
const variantStyles = {
  danger: "bg-red-500 text-white",
  warning: "bg-amber-500 text-white",
};
// так правильно — через систему
const v = colorScheme[variant]; // variant: ColorScheme
дашборд админа
дашборд админа

Фронтенд: стек и решения

Попросил иишку выплюнуть мне README проекта со всеми endpoints, чтобы другая иишка сделала чистый фронт. Я давно работал с Manus. Это иишка которая может запилить мощное web приложение с 0. Однако я 3 раза с 0 просил его переписать фронт. У меня на тот момент не было никакого опыта и понимания, как он работает. 

Поэтому отбросив весь говнокод с мусором от Manus (много мусора потому что ИИ на предикт кладет много компонентов и отладочных скриптов для себя), я начал через Claude c 0 писать фронт, причем с полным понимаем. Так я познакомился с Vite — бандлером. Это очень удобный инструмент: он не только собирает код, но и работает как локальный сервер, который по запросу браузера отдает HTML-страничку, скомпилированную из исходников (TypeScript, React-компонентов и т.д.).

Перед тем как внедрять TanStack Query и Zustand в основной проект — сделал отдельный мини-проект, максимально упрощённую копию без бизнес-логики. Разобрался там как работает поллинг, как Zustand синхронизируется с localStorage, как JWT refresh flow устроен изнутри. Только потом перенёс в основной.

Стек:

  • Vite — бандлер. В режиме разработки выступает сервером, по запросу браузера отдаёт собранную страницу. TypeScript и TSX из коробки.

  • TanStack Query — автоматическое обновление данных, кэширование, инвалидация. REST-эндпоинты опрашиваются с нужным интервалом, UI перерисовывается при изменениях.

  • Zustand + localStorage — кэширование состояния между сессиями. После перезагрузки страницы не нужен повторный вход.

  • wouter — лёгкий роутер. Значительно проще React Router, без лишних абстракций.

  • httpOnly cookie — refresh-токен, проставляется бэкендом.

список пользователей
список пользователей

Эксперимент с WebGL

В src/shaders/ лежат background.frag.glsl и background.vert.glsl — следы одного эксперимента. Мне понравился анимированный фон на странице Яндекс.Музыки, и я решил разобраться как он устроен. Открыл DevTools, нашёл в исходниках GLSL-шейдеры, скопировал и адаптировал под себя. Пришлось разобраться как работают uniform-переменные, как передавать время через requestAnimationFrame, как подключить .glsl-файлы через Vite (нужен отдельный плагин, декларация типа в glsl.d.ts).

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


Деплой: bash вместо Ansible

Весь деплой — bash-скрипт prod/deploy.sh. Накатывает Hysteria2 на ноды в четырёх странах и бэк в Yandex Cloud. Функция деплоя на YC с автоматическим продлением сертификатов через certbot:

deploy_yandex() {
  local host="$YC_HOST"
  ssh "$host" "
    cd /opt/htrbox &&
    git pull &&
    docker compose pull &&
    docker compose up -d --build
  "
  # Продление сертификата если истекает в течение 30 дней
  ssh "$host" "certbot renew --quiet --deploy-hook 'nginx -s reload'"
}

Пытался переписать на Ansible Playbooks — повяз. Для пяти серверов bash справляется отлично, а риск положить пользователям интернет кривым playbook'ом не стоил эксперимента. Это сознательное решение, а не откладывание.

Рядом лежит prod/cleanup.sh — убирает старые образы, контейнеры, сети на нодах.

Для работы с git - GitKraken. Значительно удобнее терминального tig. Для одного разработчика бесплатный.


Что сейчас и что дальше

Проект работает. Бэк отдаёт весь пул пользователей одним запросом — при росте до 100+ придётся добавить пагинацию.

У каждого пользователя в профиле — зверушка, назначаемая хешированием имени по алгоритму djb2. 12 штук, все SVG. Алгоритм детерминированный: один и тот же username всегда даёт одну и ту же зверушку.

Сейчас интегрирую платёжный шлюз — API написано, жду одобрения. Если знаете шлюзы для high-risk без необходимости оформлять ИП/СЗ, с нормальным API и Sandbox — пишите в комменты.

В планах: Prometheus + Grafana + Node Exporter для метрик нод, с отображением в профиле пользователя как бизнес-фича.

Комментарии (21)


  1. Yarus23
    22.05.2026 00:17

    Гениально!


  1. Emelian
    22.05.2026 00:17

    Такое впечатление, автора минусуют по принципу: «Ничего личного, только бизнес!».


    1. poige
      22.05.2026 00:17

      Да фиг знает, так-то вообще это — сетевая инфраструктура для реальных пользователей. А выглядит похожим на «пока ждал приёма у терапевта в платной больничке — вдохновился, а как до кассы добрался… — так вообще сразу решил свою налабать! Пока получается!!!11». Утрирую конечно и конечно масштабы возможных бедствий разные, но в целом ощущения схожие. Особенно момент перехода от «Я не готов мириться с принудительными блокировками интернета» к «наткнулся на видео Как устроена оплата картой — оно показало мне возможности … я понял - надо делать!» выглядит кринжевато.


      1. xcillx Автор
        22.05.2026 00:17

        Основная идея статьи — не VPN и не блокировки. А то, что сегодня из готовых кирпичиков можно собирать реальные продукты без коммерческого опыта за спиной. При правильном использовании ИИ отлично связывает эти кирпичики — и в голове, и в коде. Ограничения интернета — это была не причина, а спичка. Хворост уже лежал.


        1. poige
          22.05.2026 00:17

          хворост лежит уже давно. Вопрос только в том, что теперь спички продают детям.


          1. xcillx Автор
            22.05.2026 00:17

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


            1. Anarchon
              22.05.2026 00:17

              "Дураки учатся на своих ошибках, а умные — на чужих" (с)

              Детям не продают спичек и не "учат не обжигаться" на практике. Им объясняют, что спички - не игрушка.


            1. g3falsht
              22.05.2026 00:17

              Уже забудьте это, на КСО в Шестерочке вчера пришлось подтверждать возраст - покупая зажигалку. Понятно что в контексте, но все равно.


      1. poige
        22.05.2026 00:17

        (эхъ, не успел в окно редактирования, допишу отдельно — на всякий — о себе):

        1) Чё-т понимаю про сети (), но бизнес-конфликта интересов с автором — ноль. 2) Не минусовал ни статью, ни автора. 3) Из тех, кто в голосину орнул на отжиг «У меня классический айтишный бэкграунд: я 12 лет мешал людям выкатывать в продакшен то, что они называли MVP.»

        И да, «Копроэкономика» (2008-й так-то!) предупреждала нас всех очень давно. Впрочем, игра на понижение началась ещё раньше.


      1. Emelian
        22.05.2026 00:17

        Да фиг знает,

        Ну, если речь о минусах (сейчас, у вас: +16 и –12 == +5 (?), по арифметике «Хабра»), то отрицательное внимание – тоже внимание. Наверное, «минусаторы» видят в вас конкурента, потому и «злобствуют». «Работа» у них такая, молоко надо давать за «вредность» :) .

        Пока получается!!!

        Это – самое главное! Все мы должны пройти свой Путь, независимо от его результатов (как говорили древние философы: «Цель – Ничто! Путь движения к Цели – это всё!»).


        1. poige
          22.05.2026 00:17

          Не та ветка. ;)


  1. akdengi
    22.05.2026 00:17

    С учетом того, что IP VPN живут в "сервисах" от силы месяц-полтора (а то и пару дней типа MTG Proxy) и благополучно банятся как с той, так и с этой стороны, если начнут лезть на разные ИИ сервисы и не только, бизнес будет так себе. Если только у автора не пул адресов в разных локациях и он не провайдер :) Для себя да, круто (хотя тут же Амнезию2 может поставить на VPS через их мобильное приложение на VPS даже далекий от ИТ человек), а вот для коммерческого продукта начинать надо с инфраструктуры.


    1. xcillx Автор
      22.05.2026 00:17

      Я открыл GitHub-репо — при желании можно развернуть для своих, человек на 300, и жить спокойно: все пользователи проверенные, публичности ноль. Про РКН и скупку ключей — да, слышал, это реальная история с публичными сервисами. Именно поэтому закрытый круг пользователей сильно снижает риск.


  1. kuza2000
    22.05.2026 00:17

    Про план все верно. Не надо писать "промпты". Просто обсуждаешь идеи с ИИ, пока не родится хороший план, после отдаешь его на кодирование. Давно к этому пришел.


    1. Dhwtj
      22.05.2026 00:17

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

      Без шуток

      Давеча пробовал.

      Что же мне сделал LLM:

      • Преждевременная оптимизация

      • Мозаичное мышление (в частности, вместо 3 кешей сделал 8, на каждый локальный случай)

      • Защитное программирование с молчаливыми фолбеками, вместо того чтобы честно падать или хотя бы логировать странности

      • Излишнее доверие и домысливание функционала библиотек

      • Каскады неоптимальных архитектурных решений

      Я конечно попинал ему и внёс в долговременную память, но толку нет и не будет


      1. kuza2000
        22.05.2026 00:17

        У меня по другому. Хороший план реализуется чисто, и в 90-95% случаев работает сразу, без отладки.


      1. kuza2000
        22.05.2026 00:17

        У вас либо слабые модели, либо план плохой, либо язык экзотический. Других вариантов нет, без шуток))


      1. kuza2000
        22.05.2026 00:17

        • Мозаичное мышление (в частности, вместо 3 кешей сделал 8, на каждый локальный случай)

        Ну для этого и нужен план. Это видно в плане, он корректируется.

        • Защитное программирование с молчаливыми фолбеками, вместо того чтобы честно падать или хотя бы логировать странности

        Это да, меня тоже доставало) Добавил в правила, что бы не делал так. Ну, у меня еще мемори-банк, там тоже многое архитектурное описано.


        1. Dhwtj
          22.05.2026 00:17

          Что такое план?

          Если LLM будет писать план, оперируя в нём своими абстракциями, то читать его мне будет тяжело, а ошибочно принятых ADR будет много

          Если я пишу "план" то это значит я сделал уже почти всю работу по проектированию. Дописать код после этого могу я, могу LLM позвать - это уже неважно и времени почти не экономит.


  1. DamirMur
    22.05.2026 00:17

    Интересно, где используются 15 Google-аккаунтов, какой провайдер.Пользоваться моделями Гугла напрямую, в Гугл аи студио, он почти сразу палит


  1. xcillx Автор
    22.05.2026 00:17

    Я пробовал Google AI Studio, но там проблема простая — гугл банит по IP. Через месяц ключ становился недействительным.

    15 аккаунтов использовались для Claude — на тот момент (март 2026) самая удобная модель с большим бесплатным лимитом токенов и поддержкой tar-архивов. Под неё и писался ./pack.sh.

    Позже эти же аккаунты пригодились для Codex. После версии 5.5 он отлично подбирает UI-стили, так что начал комбинировать оба инструмента. Критерий простой: если модель "поплыла" или плохо держит контекст — не трачу время на переформулировку задачи, просто переключаюсь. Codex читает контекст из git-репозитория сам через CLI — достаточно дать доступ к папке проекта, никакого ./pack.sh не нужно.

    Плюс Claude в другом: ты сам копируешь изменённые файлы и применяешь через буфер обмена. Шаг за шагом, в реальном времени видишь что именно и зачем он делает. Для меня как для новичка это оказался идеальный формат — максимальный контроль и понимание каждого действия.

    Если хочется просто повайбкодить как Peter Steinberger и быстро посмотреть как идея выглядит на уровне MVP, то философия моего подхода отличается.