$ sudo teach|
    $ sudo teach|
    IT school
  • Telegram
  • Партнёрам
  • Все курсы
$ sudo teach IT
OOO "SALEPROFIT"Контакты и реквизитыIT-Park Logo

Школа

  • Блог
  • Проверить сертификат

Сотрудничество

  • Стать учителем
  • Партнёрская программа
  • О проекте

Право

  • Оферта
  • Политика конфиденциальности

© 2023–2026 $ sudo teach IT™. All Rights Reserved. Public user contributions licensed under CC BY-SA 4.0 license with attribution required
TelegramGitHubYouTube
ГлавнаяБлогПишем CLI для разработчика на Python: команды, конфиги и корректные exit codes по стандарту проекта

Пишем CLI для разработчика на Python: команды, конфиги и корректные exit codes по стандарту проекта

$ sudo teach IT
·15 сентября 2026 г.·12 мин·103
Пишем CLI для разработчика на Python: команды, конфиги и корректные exit codes по стандарту проекта

Построим удобный интерфейс командной утилиты: структура подкоманд, чтение конфигов, валидация входных параметров и единая логика ошибок с понятными exit code. Сфокусируемся на UX для тех, кто запускает CLI в CI и в скриптах.

Содержание
1) Что должен уметь CLI «по стандарту проекта»1.1. Быстро отвечать и быть детерминированным1.2. Иметь семантику exit codes, которую понимают люди и автоматизация1.3. Давать пользователю «следующий шаг»2) Архитектура: команды, конфиги и единая модель ошибок2.1. Командная структура: подкоманды как «глаголы»2.2. Конфиг: источник правды и приоритеты2.3. Единая модель ошибок3) Технический выбор: Typer и почему он удобен именно для UX4) Реализация: каркас проекта и типизация конфигурации4.1. Пример структуры файлов4.2. Модель конфигурации5) Единая модель ошибок и exit code mapping6) Сама CLI-утилита: конфиги, валидация и корректные exit codes6.1. Настройка логирования под CI6.2. Простейшее приложение (бизнес-логика)6.3. Переходим к CLI: команды, общий обработчик ошибок, валидация6.4. Почему обработчик ошибок вынесен в entrypoint7) Проверка поведения: как выглядит UX в сценариях CI7.1. Неверный параметр (валидация)7.2. Конфиг отсутствует7.3. Внутренняя ошибка8) Типичные ошибки при проектировании CLI (и как их избежать)Ошибка 1: смешивать “ошибку использования” и “ошибку валидации”Ошибка 2: не фиксировать таблицу exit codes в проектеОшибка 3: выводить ошибки в stdoutОшибка 4: менять формат сообщений и терять диагностируемостьОшибка 5: отсутствие режима “машиночитаемого вывода”9) Рекомендации по улучшению: конфиги, переменные окружения и версии схемы9.1. Версионирование схемы конфигурации9.2. Переменные окружения9.3. Строгая валидация путей10) Итог: что вы получили и как применить в своём проекте

Командные утилиты для разработчиков живут в «суровой среде»: их запускают из терминала руками, но чаще — в CI, pre-commit хуках, скриптах деплоя, пайплайнах тестирования. В такой реальности UX — это не только «красивый help». Это предсказуемость поведения, стабильные форматы ошибок, корректные сообщения для человека и, главное, строгая семантика exit code для машины.

В этой статье соберём каркас CLI на Python с фокусом на практические моменты:

  • структура команд (подкоманды),
  • чтение и валидация конфигов,
  • единый подход к ошибкам и понятным exit code,
  • поведение в CI и скриптах (что критично для команд разработчиков),
  • типичные ошибки, из‑за которых «вроде работает, но ломается в пайплайне».

Мы не будем привязываться к конкретному проекту — рассмотрим универсальный дизайн, который легко адаптировать под вашу кодовую базу.


1) Что должен уметь CLI «по стандарту проекта»

Перед кодом полезно договориться о требованиях. Хороший CLI для разработчика обычно должен:

1.1. Быстро отвечать и быть детерминированным

  • одинаковые входные параметры → одинаковые ошибки,
  • одинаковые ошибки → одинаковые exit codes,
  • при фатальных проблемах (не смогли прочитать конфиг, сломалась сеть, нет доступа к файлам) — понятное сообщение, чтобы оператор/CI мог действовать.

1.2. Иметь семантику exit codes, которую понимают люди и автоматизация

Один из наиболее частых антипаттернов: «на всё просто return 1». Это превращает CI в гадание.

Хорошая практика — завести таблицу exit code в README/CONTRIBUTING и держать её неизменной. Например:

  • 0 — успех.
  • 1 — универсальная ошибка выполнения (не “входная”, а внутренняя).
  • 2 — ошибка использования CLI (неверные аргументы/параметры/формат).
  • 3 — конфигурация: отсутствует, повреждена, несовместима с версией.
  • 4 — валидация входных параметров (например, неверный диапазон).
  • 5 — ошибка сети/внешних сервисов (таймаут, 5xx) — если ваш CLI ходит во внешку.
  • 6 — файл/файловая система (нет прав, директория не найдена, нет места).
  • 7 — частичный успех (например, часть чанков обработана, часть нет) — опционально.

Важно: exit code — это API. Любое изменение семантики со временем становится источником «необъяснимых» регрессий в пайплайнах.

1.3. Давать пользователю «следующий шаг»

Сообщение об ошибке должно отвечать на вопросы:

  • что не так,
  • где именно,
  • как исправить,
  • (опционально) что предпринять в CI (например, подсказка про переменные окружения).

2) Архитектура: команды, конфиги и единая модель ошибок

Построим утилиту вокруг трёх слоёв:

  1. CLI слой: парсинг аргументов, структура команд и показ help.
  2. Сервисный слой: загрузка/валидация конфигурации, выполнение бизнес-логики.
  3. Слой ошибок и exit codes: единое преобразование исключений в человекочитаемые сообщения и числовые exit codes.

2.1. Командная структура: подкоманды как «глаголы»

Обычно полезная структура:

  • tool config — управление конфигом (проверка схемы, печать примера),
  • tool run — основной сценарий,
  • tool validate — валидация входных параметров/конфигурации,
  • tool version — версия утилиты.

Подкоманды уменьшают число опциональных параметров и делают UX спокойнее.

2.2. Конфиг: источник правды и приоритеты

Конфиг обычно формируется из нескольких источников:

  • YAML/JSON-файл из проекта (например ./tool.yaml),
  • путь --config,
  • переменные окружения TOOL_*.

Нужно явно определить приоритеты:

  1. CLI аргументы (если пользователь явно указал)
  2. переменные окружения
  3. конфиг-файл
  4. значения по умолчанию

Если приоритет не прописан, вы получите ситуации «локально работает, в CI — нет».

2.3. Единая модель ошибок

Вместо того чтобы позволять библиотеке парсинга аргументов выбрасывать разрозненные исключения, стоит иметь собственный набор исключений:

  • CliUsageError — ошибка использования (невалидные аргументы/формат)
  • ConfigError — проблемы с конфигом (не найден, не парсится, несовместимая версия)
  • ValidationError — валидация входных параметров/конфига (например, max_retries < 0)
  • ExternalError — внешние сервисы
  • FilesystemError — доступы/файлы

И в одном месте перевести это в exit code и сообщения.


3) Технический выбор: Typer и почему он удобен именно для UX

Для Python CLI есть несколько вариантов: argparse, click, typer. Мы выберем Typer, потому что:

  • он даёт современный DX: аннотации типов → подсказки и help,
  • поддерживает подкоманды и опции,
  • корректно работает с Python 3 и типами,
  • позволяет централизованно обработать исключения и выставлять exit codes.

Если вы хотите глубже разобраться в нюансах Typer (модели данных, более сложные кейсы параметров, паттерны структуры проекта), можно обратить внимание на курс Typer PRO — но сам по себе он не заменяет дизайн exit codes и ошибок, о чём дальше.


4) Реализация: каркас проекта и типизация конфигурации

4.1. Пример структуры файлов

Предположим, проект выглядит так:

code
mytool/
  pyproject.toml
  mytool/
    __init__.py
    cli.py
    app.py
    config.py
    errors.py
    logging_config.py
  • cli.py — Typer команды и опции.
  • config.py — загрузка конфигов, схема и валидация.
  • errors.py — определение исключений и mapping в exit code.
  • app.py — “бизнес” (демо) и вызовы сервисов.
  • logging_config.py — настройка логирования под CI.

4.2. Модель конфигурации

Реалистичный подход — описывать конфиг структурно. Можно использовать pydantic, но если у вас уже есть другой валидатор — адаптируйте идею.

Пример через Pydantic (для демонстрации валидации и удобства):

code
# mytool/config.py
from __future__ import annotations

from pathlib import Path
from typing import Any, Optional

import yaml
from pydantic import BaseModel, Field, ValidationError


class ToolConfig(BaseModel):
    version: int = Field(1, description="Схема конфигурации")
    retries: int = Field(3, ge=0, le=10)
    timeout_seconds: int = Field(30, gt=0, le=300)
    output_dir: Path = Field(..., description="Куда писать результаты")


def load_config(path: Path) -> ToolConfig:
    if not path.exists():
        raise FileNotFoundError(str(path))
    raw: Any
    try:
        raw = yaml.safe_load(path.read_text(encoding="utf-8"))
    except Exception as e:  # noqa: BLE001
        raise ValueError(f"Не удалось прочитать YAML: {e}") from e

    try:
        cfg = ToolConfig.model_validate(raw)
    except ValidationError as e:
        raise ValueError(f"Конфиг не соответствует схеме: {e}") from e

    # Сопоставление версии схемы — полезно для долгоживущих проектов.
    if cfg.version != 1:
        raise ValueError(f"Несовместимая версия схемы конфига: {cfg.version} (ожидалось 1)")

    return cfg

Обратите внимание: мы сознательно превращаем ошибки парсинга/валидации в ValueError и FileNotFoundError. Но финальную типизацию ошибок лучше делать в слой errors.py, чтобы CLI единообразно выставлял exit code. В статье ниже мы это сделаем.


5) Единая модель ошибок и exit code mapping

Определим исключения и таблицу exit codes.

code
# mytool/errors.py
from __future__ import annotations

from dataclasses import dataclass
from typing import Type


class CliError(Exception):
    """Базовое исключение CLI."""

    exit_code: int = 1
    user_message: str = "Ошибка выполнения"

    def __init__(self, message: str | None = None) -> None:
        super().__init__(message or self.user_message)


class CliUsageError(CliError):
    exit_code = 2
    user_message = "Неверное использование CLI"


class ConfigError(CliError):
    exit_code = 3
    user_message = "Проблема с конфигурацией"


class ValidationError(CliError):
    exit_code = 4
    user_message = "Ошибка валидации параметров"


class ExternalError(CliError):
    exit_code = 5
    user_message = "Ошибка внешнего сервиса"


class FilesystemError(CliError):
    exit_code = 6
    user_message = "Проблема с файловой системой"


@dataclass(frozen=True)
class ErrorView:
    exit_code: int
    message: str


def to_error_view(exc: Exception) -> ErrorView:
    # Нормализуем типы исключений в единый формат.
    if isinstance(exc, CliError):
        return ErrorView(exit_code=exc.exit_code, message=str(exc) or exc.user_message)

    # Примеры "обвязки" для системных исключений:
    if isinstance(exc, FileNotFoundError):
        return ErrorView(exit_code=6, message=f"Файл не найден: {exc}")

    if isinstance(exc, PermissionError):
        return ErrorView(exit_code=6, message=f"Нет прав: {exc}")

    # Любая неожиданность = внутренняя ошибка.
    # В CI полезно дать минимальное сообщение пользователю,
    # а детали логировать.
    return ErrorView(exit_code=1, message="Внутренняя ошибка. Проверьте логи для деталей.")

Ключевой момент: to_error_view решает, что показывать пользователю и какой exit code отдавать. Подробности ошибок (stack trace) — в логах.


6) Сама CLI-утилита: конфиги, валидация и корректные exit codes

6.1. Настройка логирования под CI

В CI обычно важна предсказуемость формата. Минимальный вариант — в логах текст, а в stdout/stderr — сообщения об ошибках.

code
# mytool/logging_config.py
from __future__ import annotations

import logging
import sys


def configure_logging(verbosity: int) -> None:
    level = logging.WARNING
    if verbosity == 1:
        level = logging.INFO
    elif verbosity >= 2:
        level = logging.DEBUG

    logging.basicConfig(
        level=level,
        format="%(asctime)s %(levelname)s %(name)s: %(message)s",
        stream=sys.stderr,
    )

6.2. Простейшее приложение (бизнес-логика)

Для примера сделаем команду run, которая “использует” конфиг и параметры.

code
# mytool/app.py
from __future__ import annotations

import logging
from pathlib import Path

from .config import ToolConfig
from .errors import ExternalError, FilesystemError, ValidationError

logger = logging.getLogger(__name__)


def run_task(cfg: ToolConfig, *, dry_run: bool, name: str) -> int:
    # Валидация входных параметров можно делать тут
    # (или на уровне CLI/конфига — где вам удобнее).
    if not name.strip():
        raise ValidationError("Параметр --name не может быть пустым")

    out_dir: Path = cfg.output_dir
    if not dry_run:
        try:
            out_dir.mkdir(parents=True, exist_ok=True)
        except OSError as e:
            raise FilesystemError(f"Не удалось создать директорию вывода: {out_dir}") from e

    logger.info("Старт задачи: name=%s retries=%s timeout=%s dry_run=%s",
                name, cfg.retries, cfg.timeout_seconds, dry_run)

    if name.lower() == "fail":
        # Демонстрация: внешняя/внутренняя ошибка.
        raise ExternalError("Симуляция сбоя внешнего шага для демонстрации exit code")

    # Возвращаем количество обработанных элементов (для демонстрации).
    return 1

6.3. Переходим к CLI: команды, общий обработчик ошибок, валидация

Главная цель — в одном месте перехватывать CliError и выставлять exit code. В Typer для этого можно использовать try/except вокруг app() или обработчик typer.Exit.

Мы сделаем явный “entrypoint” с единым обработчиком:

code
# mytool/cli.py
from __future__ import annotations

from pathlib import Path
from typing import Optional

import typer

from .app import run_task
from .config import ToolConfig, load_config
from .errors import (
    CliError,
    CliUsageError,
    ConfigError,
    ValidationError,
    to_error_view,
)
from .logging_config import configure_logging

app = typer.Typer(add_completion=False, help="mytool — CLI для разработчиков")


def resolve_config(
    config_path: Optional[Path],
) -> ToolConfig:
    """
    Разрешение конфигурации:
    - если путь явно задан --config, читаем его;
    - иначе пробуем tool.yaml в текущей директории.
    """
    if config_path is None:
        config_path = Path.cwd() / "tool.yaml"

    try:
        return load_config(config_path)
    except FileNotFoundError as e:
        raise ConfigError(f"Не найден конфиг: {config_path}") from e
    except ValueError as e:
        raise ConfigError(str(e)) from e
    except Exception as e:  # noqa: BLE001
        raise ConfigError(f"Ошибка загрузки конфига: {config_path}") from e


@app.callback(invoke_without_command=False)
def main(
    ctx: typer.Context,
    verbosity: int = typer.Option(0, "--verbose", "-v", count=True, help="Уровень логирования"),
) -> None:
    configure_logging(verbosity)


@app.command()
def version() -> None:
    """Показать версию утилиты."""
    # Здесь можно подставить importlib.metadata.version(...)
    typer.echo("mytool 0.1.0")


@app.command()
def validate(
    config: Optional[Path] = typer.Option(
        None, "--config", "-c", exists=False, dir_okay=False, file_okay=True,
        help="Путь к конфигу YAML (по умолчанию: ./tool.yaml)"
    ),
) -> None:
    """Проверить конфигурацию на валидность."""
    cfg = resolve_config(config)
    # Если дошли сюда — конфиг валиден.
    typer.echo(f"OK: config.version={cfg.version}, retries={cfg.retries}, timeout={cfg.timeout_seconds}, output_dir={cfg.output_dir}")


@app.command()
def run(
    name: str = typer.Option(..., "--name", "-n", help="Имя задачи для запуска"),
    dry_run: bool = typer.Option(False, "--dry-run", help="Не выполнять побочные эффекты"),
    config: Optional[Path] = typer.Option(
        None, "--config", "-c", help="Путь к конфигу YAML (по умолчанию: ./tool.yaml)"
    ),
) -> None:
    """Запустить основную задачу."""
    # Пример валидации параметра на уровне CLI:
    # (можно перенести в app.run_task, но тогда exit code mapping будет одинаковый)
    if len(name) > 200:
        # Важно: это не “ошибка парсинга аргументов”, а именно “валидация”.
        raise ValidationError("Параметр --name слишком длинный (макс. 200 символов)")

    cfg = resolve_config(config)
    processed = run_task(cfg, dry_run=dry_run, name=name)
    typer.echo(f"Done: processed={processed}")


def entrypoint() -> None:
    """
    Единая точка входа:
    - перехватывает ошибки проекта,
    - преобразует в понятный stderr-вывод,
    - выставляет корректный exit code.
    """
    try:
        app()  # Typer сам прочитает sys.argv и выполнит команду.
    except Exception as exc:  # noqa: BLE001
        view = to_error_view(exc)
        # Для UX: сообщение об ошибке — в stderr.
        typer.secho(view.message, err=True, fg=typer.colors.RED)
        raise typer.Exit(code=view.exit_code)


if __name__ == "__main__":
    entrypoint()

6.4. Почему обработчик ошибок вынесен в entrypoint

Если обрабатывать ошибки “разрозненно” внутри каждой команды, вы быстро получите:

  • несогласованные exit codes,
  • разные форматы ошибок,
  • забытые кейсы (где-то исключение пробрасывается наверх и Typer отдаёт свой exit code — чаще всего это 1).

Единая точка входа решает это системно.


7) Проверка поведения: как выглядит UX в сценариях CI

Рассмотрим типовые сценарии.

7.1. Неверный параметр (валидация)

Допустим, --name слишком длинный. Мы выбросили ValidationError с exit code 4. В CI будет:

  • stderr: Параметр --name слишком длинный (макс. 200 символов)
  • exit code: 4

Это удобно: вы можете в CI различать «сбой инфраструктуры» (6, 5) и «неправильные параметры» (2, 4) без разбора текста.

7.2. Конфиг отсутствует

Если tool.yaml не найден, resolve_config возбуждает ConfigError → exit code 3. Сообщение содержит путь, что экономит время расследования.

7.3. Внутренняя ошибка

Неожиданное исключение превращается в exit code 1 и сообщение “Внутренняя ошибка…”. При этом stack trace уйдёт в лог благодаря logging. Это тот случай, когда пользователь не обязан видеть весь traceback, но техподдержка — должна.


8) Типичные ошибки при проектировании CLI (и как их избежать)

Ошибка 1: смешивать “ошибку использования” и “ошибку валидации”

Если пользователь передал неправильный формат параметра, это ошибка использования (2) или валидации (4) — но выберите и придерживайтесь подхода. Например:

  • --timeout abc (не число) → обычно ошибка использования (парсинг).
  • --timeout -5 (число, но не по правилам) → ошибка валидации (4).

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

Ошибка 2: не фиксировать таблицу exit codes в проекте

Без документации таблица начинает “плыть”. Через полгода вы не сможете ответить, почему CI ожидал 3, а фактически получал 1. Минимально — держите таблицу в README или docs/cli.md.

Ошибка 3: выводить ошибки в stdout

CI и скрипты часто читают stdout (например, для парсинга JSON). Поэтому:

  • текст ошибок и предупреждений — в stderr,
  • данные “для машины” — в stdout (если вы вообще делаете machine-readable режим).

В нашем примере ошибки через typer.secho(..., err=True) попадают в stderr.

Ошибка 4: менять формат сообщений и терять диагностируемость

Если ваше сообщение об ошибке исчезает из логов или меняется каждые 2 релиза, расследование будет дороже. Можно смягчить это:

  • сообщения стабильного шаблона (например, “Не найден конфиг: …”),
  • детали — в логах.

Ошибка 5: отсутствие режима “машиночитаемого вывода”

Если ваш CLI используется в пайплайнах, часто нужен --output json или отдельная команда report. Тогда важно, чтобы exit code оставался прежним, а формат stdout был предсказуемым. В статье мы не разворачиваем это, но дизайн ошибки/exit code мы уже сделали универсальным.


9) Рекомендации по улучшению: конфиги, переменные окружения и версии схемы

Чтобы CLI реально жил в проектах, стоит добавить несколько практик.

9.1. Версионирование схемы конфигурации

В примере мы держали version в конфиге и жёстко проверяли cfg.version != 1. Альтернатива — делать миграции. Но даже если миграций нет, версия схемы даёт контроль.

9.2. Переменные окружения

Если хотите поддержать переопределения из окружения, полезно:

  • определить ключи вида MYTOOL_RETRIES, MYTOOL_TIMEOUT_SECONDS,
  • явно указать приоритет (как выше),
  • валидировать значения окружения теми же правилами, что и конфиг.

9.3. Строгая валидация путей

Для output_dir важно решать: создавать директории автоматически или требовать существование. В CI чаще удобно создавать, а в “боевом” запуске — аккуратно проверять доступ.


10) Итог: что вы получили и как применить в своём проекте

Мы собрали рабочий каркас Python CLI с предсказуемым поведением:

  • подкоманды (version, validate, run),
  • конфиг по приоритету (по умолчанию ./tool.yaml, при желании — --config),
  • валидация входных параметров,
  • единое преобразование исключений в понятные exit codes,
  • корректный UX для CI: ошибки в stderr, код возврата — стабильный “контракт” для автоматизации.

Главный инженерный смысл здесь не в конкретных командах, а в дисциплине:

  1. exit codes должны быть документированы и стабильны;
  2. ошибки должны маппиться в exit codes в одном месте;
  3. конфиг должен иметь чёткую схему и версию;
  4. вывод ошибок должен быть пригоден для машинного и человеческого чтения.

Если вы хотите прокачать именно Typer-сторонy (как лучше организовать параметры, типы, сложные сценарии подкоманд и “эргономику” help), хороший следующий шаг — изучить Typer PRO. Но даже без него приведённая в статье архитектура даст вам тот самый уровень надёжности, который требуется CLI в CI и в скриптах.

Войдите, чтобы поставить лайк и оставить комментарий.

Автор

$ sudo teach IT

Typer PRO

Курс по теме

Typer PRO

С нуля до уверенной работы с CLI-проектами на Python. Подходит для начинающих и тех, кто уже пишет на Python. Разберитесь с Typer раз и навсегда — и перестаньте копировать команды вслепую.

Открыть курс

Продолжите обучение

Все курсы
Python – для начинающих!

Python – для начинающих!

С нуля до профессионального уровня. Подходит для всех. Учитесь каждый день и овладейте самым популярным языком программирования.

Перейти к курсу
Простыми словами о защищенном соединении

Простыми словами о защищенном соединении

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

Перейти к курсу
Ботостроение Telegram

Ботостроение Telegram

Лёгкий, быстрый и доступный способ познакомиться с миром ботостроения в Telegram. Видео, конспекты, практика и помощь – всё у нас на курсе.

Перейти к курсу
Сервис, который не падает - как построить безопасную зашифрованную сеть

Сервис, который не падает - как построить безопасную зашифрованную сеть

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

Перейти к курсу

Другие статьи

Python для аналитики на практике: как строить признаки и проверять их воспроизводимость
python

Python для аналитики на практике: как строить признаки и проверять их воспроизводимость

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

17 сентября 2026 г.
1050
Системный дебаг памяти и производительности в Python: где искать утечки и как проверять гипотезы
системный

Системный дебаг памяти и производительности в Python: где искать утечки и как проверять гипотезы

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

15 сентября 2026 г.
1030
Создание Telegram бота в 2026 легко и просто! Полный курсы!
создание

Создание Telegram бота в 2026 легко и просто! Полный курсы!

19 июня 2026 г.
1431
Как выбрать, куда идти в IT: веб, боты, данные — без хаоса
выбрать

Как выбрать, куда идти в IT: веб, боты, данные — без хаоса

Разберём самые понятные направления для новичка (веб, боты, аналитика/данные) и чем они отличаются по шагам обучения. В конце вы получите простой план, как проверить, что вам подходит, и какой стартовый путь выбрать.

5 октября 2026 г.
580
Путь от README до демо: как собрать портфолио разработчика, чтобы его хотели смотреть
путь

Путь от README до демо: как собрать портфолио разработчика, чтобы его хотели смотреть

Разберём структуру репозитория, как упаковать проект в понятную историю (проблема → решение → запуск → метрики качества) и какие артефакты повышают доверие: скринкасты, архитектурная схема, чек-лист продакшн-готовности.

17 сентября 2026 г.
1090
Как понять, какой язык программирования учить первым: простой тест и выбор
понять

Как понять, какой язык программирования учить первым: простой тест и выбор

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

2 октября 2026 г.
630

Комментарии

Станьте первым в обсуждении

Поделитесь мнением о статье или задайте вопрос автору

Содержание

1) Что должен уметь CLI «по стандарту проекта»1.1. Быстро отвечать и быть детерминированным1.2. Иметь семантику exit codes, которую понимают люди и автоматизация1.3. Давать пользователю «следующий шаг»2) Архитектура: команды, конфиги и единая модель ошибок2.1. Командная структура: подкоманды как «глаголы»2.2. Конфиг: источник правды и приоритеты2.3. Единая модель ошибок3) Технический выбор: Typer и почему он удобен именно для UX4) Реализация: каркас проекта и типизация конфигурации4.1. Пример структуры файлов4.2. Модель конфигурации5) Единая модель ошибок и exit code mapping6) Сама CLI-утилита: конфиги, валидация и корректные exit codes6.1. Настройка логирования под CI6.2. Простейшее приложение (бизнес-логика)6.3. Переходим к CLI: команды, общий обработчик ошибок, валидация6.4. Почему обработчик ошибок вынесен в entrypoint7) Проверка поведения: как выглядит UX в сценариях CI7.1. Неверный параметр (валидация)7.2. Конфиг отсутствует7.3. Внутренняя ошибка8) Типичные ошибки при проектировании CLI (и как их избежать)Ошибка 1: смешивать “ошибку использования” и “ошибку валидации”Ошибка 2: не фиксировать таблицу exit codes в проектеОшибка 3: выводить ошибки в stdoutОшибка 4: менять формат сообщений и терять диагностируемостьОшибка 5: отсутствие режима “машиночитаемого вывода”9) Рекомендации по улучшению: конфиги, переменные окружения и версии схемы9.1. Версионирование схемы конфигурации9.2. Переменные окружения9.3. Строгая валидация путей10) Итог: что вы получили и как применить в своём проекте