$ 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
·10 августа 2026 г.·13 мин·36
Пишем CLI-приложение для разработчиков на Python: команды, прогресс, exit codes и конфиги без магии

Разберём архитектуру CLI-проекта: как проектировать команды и опции, как правильно возвращать exit codes, отображать прогресс, хранить конфиги и обеспечивать повторяемость запусков. В конце — шаблон структуры репозитория под дальнейшее расширение.

Содержание
1) Базовые принципы CLI, которые спасают время1.1. CLI — это контракт, а не удобство для автора1.2. stdout для данных, stderr для диагностики1.3. Exit codes — часть UX для автоматизации2) Архитектура CLI-проекта: команды как единицы ответственности2.1. Минимальный каркас: парсер отдельно от логики3) Проектирование команд и опций без путаницы3.1. Команды как функции: один вход — понятные параметры3.2. Типы параметров и валидация3.3. Уровни сложности: конфиг + переопределения4) Exit codes: как возвращать их правильно и последовательно4.1. Пример матрицы exit codes4.2. Важно: отделяйте ошибки разбора и ошибки выполнения4.3. Реализация: единый механизм завершения5) Прогресс: когда нужен, как сделать аккуратно и не сломать логирование5.1. Общие правила прогресса5.2. Практический подход: Rich как слой визуализации5.3. Сопоставление прогресса с логами6) Конфигурации без магии: источники, приоритеты и воспроизводимость6.1. Формат и модель конфигурации6.2. Источники конфигурации и приоритеты6.3. Явный merge и следы источников6.4. Репродуцируемость: фиксируйте окружение и артефакты7) Как это связать в целую команду: пример мини-CLI8) Typer/Click/argparse: что выбрать и как не ошибиться9) Типичные ошибки в CLI (и как их избежать)9.1. «exit code всегда 1»9.2. Смешивание stdout и stderr9.3. Неочевидный merge конфигурации9.4. Конфиги меняют поведение без версии9.5. Прогресс «всегда включён»10) Шаблон структуры репозитория под расширениеПочему это работаетВывод: CLI как инженерный продукт, а не набор скриптов

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

В этой статье разберём, как проектировать CLI-приложение на Python «без магии»: как строить команды и опции, как корректно возвращать exit codes, как отображать прогресс, как хранить конфигурации и добиваться повторяемости запусков. В конце соберём шаблон структуры репозитория, который можно масштабировать под новые команды.


1) Базовые принципы CLI, которые спасают время

Прежде чем выбирать библиотеку (Click, Typer, argparse, Rich и т. п.), полезно договориться о правилах игры.

1.1. CLI — это контракт, а не удобство для автора

Если вы делаете mytool build --watch, то поведение должно быть:

  • детерминированным (при одинаковых входах результат одинаков);
  • предсказуемым при ошибках (и в человекочитаемом виде, и в машине);
  • стабильным по интерфейсу (флаги и команды не исчезают без периода совместимости);
  • совместимым с пайплайнами и логированием (особенно stdout/stderr).

1.2. stdout для данных, stderr для диагностики

Общее правило:

  • на stdout — то, что потенциально можно использовать дальше: отчёты, артефакты в текстовом виде, JSON;
  • на stderr — сообщения о ходе работы, предупреждения, ошибки, подсказки.

Это важно для CI: ошибки не «загрязняют» пайплайн вывода.

1.3. Exit codes — часть UX для автоматизации

Человек увидит текст ошибки. Автоматизация — увидит код завершения. Неправильные exit codes ломают сборку, усложняют диагностику и делают систему «хрупкой».

Нужно заранее определить маппинг: что означает каждый код.


2) Архитектура CLI-проекта: команды как единицы ответственности

Хороший CLI устроен вокруг логических операций, а не вокруг парсинга аргументов. Разделите три слоя:

  1. Интерфейс (команды/опции/валидаторы): отвечает за схему ввода.
  2. Сервисный слой: выполняет бизнес-операции (например, собрать проект, прогнать генерацию, выгрузить артефакты).
  3. Интеграции: файлы, сеть, файловая система, вызовы внешних программ, кэш, модули.

Тогда вы легко тестируете: бизнес-логику без CLI, и CLI — отдельно.

2.1. Минимальный каркас: парсер отдельно от логики

Допустим, вы проектируете команду init, которая создаёт конфиг. Правильная структура:

  • cli/commands.py — объявление команды и параметров;
  • core/config.py — модель конфигурации;
  • core/init.py — функция run_init(...).

Схема файлов будет позже в шаблоне репозитория.


3) Проектирование команд и опций без путаницы

Команды CLI обычно делятся на две категории:

  • операции верхнего уровня (build, test, deploy, init);
  • служебные операции (doctor, version, completion).

3.1. Команды как функции: один вход — понятные параметры

Параметры команды должны быть:

  • именованными (опциями), а не «позиционными всем подряд»;
  • с адекватными типами;
  • валидируемыми.

Например:

  • --config путь к конфигу;
  • --env имя окружения (dev, prod);
  • --output-dir директория для результатов;
  • --jobs число параллельных задач.

Избегайте ситуации, когда build принимает десять позиционных аргументов — это делает CLI сложным для автоматизации и опасным для людей.

3.2. Типы параметров и валидация

Даже если вы используете библиотеку, важно понимать принципы:

  • Строки должны валидироваться на уровне формата (path exists, url, semver).
  • Числа — ограничиваться (--jobs >= 1).
  • Флаги — иметь понятный дефолт и эффект.

Практика: валидировать «на границе» (в CLI), а не в глубине логики.

3.3. Уровни сложности: конфиг + переопределения

Комфортная схема запуска разработчиков:

  1. есть базовая конфигурация (файл, например tool.toml или config.yaml);
  2. есть окружение (--env dev);
  3. есть аргументы CLI как override (--jobs 8, --output-dir ...).

Это позволяет сохранять повторяемость (конфиг фиксирует поведение), но не заставляет переписывать команду целиком.


4) Exit codes: как возвращать их правильно и последовательно

Проблема большинства проектов: exit codes описаны «на словах» или возвращаются как попало. В итоге CI получает «код 1» на всё подряд, и вы теряете диагностируемость.

4.1. Пример матрицы exit codes

Пример практичной политики для разработческого CLI:

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

Смысл: коду соответствует класс проблем.

4.2. Важно: отделяйте ошибки разбора и ошибки выполнения

  • Если аргументы невалидны — это «ошибка интерфейса» → код 2.
  • Если конфиг не найден / неверный — код 3.
  • Если задача не выполнилась (командой вызвали внешний процесс или произошла ошибка в бизнес-логике) — код 4 и так далее.

4.3. Реализация: единый механизм завершения

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

Пример на «обычных» исключениях без привязки к конкретной библиотеке:

code
# core/errors.py
class CLIUsageError(Exception):
    """Неправильные аргументы или некорректные параметры команды."""

class ConfigError(Exception):
    """Конфигурация отсутствует, повреждена или противоречива."""

class ExecutionError(Exception):
    """Ошибка выполнения бизнес-задачи (внешние команды, IO, сеть)."""

class TimeoutError(Exception):
    """Операция превысила лимит времени."""
code
# cli/runner.py
import sys
from core.errors import (
    CLIUsageError,
    ConfigError,
    ExecutionError,
    TimeoutError,
)

EXIT_CODES = {
    CLIUsageError: 2,
    ConfigError: 3,
    ExecutionError: 4,
    TimeoutError: 5,
}

def run_with_exit_code(fn):
    try:
        return fn()
    except Exception as e:
        code = EXIT_CODES.get(type(e), 1)
        # Не печатаем "трассировку" по умолчанию — зависит от флага --debug
        print(str(e), file=sys.stderr)
        sys.exit(code)

В реальном проекте добавляют --debug, чтобы при желании показывать traceback, но при «нормальных» запусках — держать поведение предсказуемым.


5) Прогресс: когда нужен, как сделать аккуратно и не сломать логирование

Прогресс-бары и спиннеры полезны, но легко превратить CLI в «шумный» интерфейс, который ломает пайплайны и сбор логов. Ключ: прогресс — опциональный и адаптированный к среде.

5.1. Общие правила прогресса

  • Если stdout перенаправлен в файл/пайплайн, прогресс должен отключаться или переходить в «тихие» сообщения.
  • Не печатайте прогресс в stdout, лучше использовать отдельный рендер или stderr.
  • Для долгих операций полезны:
    • индикатор выполнения (сколько сделано/сколько осталось),
    • оценки времени,
    • сообщения о текущем шаге.

5.2. Практический подход: Rich как слой визуализации

В Python популярна библиотека Rich, которая умеет рендерить прогресс и корректно работать с терминалом. Пример «скелета» с прогрессом:

code
# core/progress.py
from time import sleep
from rich.progress import Progress, SpinnerColumn, BarColumn, TimeElapsedColumn

def do_work_with_progress(tasks: int = 10) -> None:
    with Progress(
        SpinnerColumn(),
        BarColumn(),
        TimeElapsedColumn(),
    ) as progress:
        task_id = progress.add_task("Processing", total=tasks)

        for _ in range(tasks):
            # имитируем работу
            sleep(0.3)
            progress.advance(task_id, 1)

Но важно: в настоящем проекте прогресс должен управляться контекстом CLI:

  • включать/выключать через --progress {auto, on, off};
  • отключать при not sys.stderr.isatty() (или аналогичной логике).

5.3. Сопоставление прогресса с логами

Если ваша команда логирует по шагам (например, в --debug), то прогресс — это только визуальный слой. Логи должны быть отдельным источником истины.

Хороший паттерн:

  • прогресс показывает краткое состояние;
  • детали уходят в лог (файл или stderr в «нормальном» режиме).

6) Конфигурации без магии: источники, приоритеты и воспроизводимость

Конфиг — то место, где «магия» обычно расползается: где-то прочитали переменные окружения, где-то — параметры CLI, где-то — дефолты из кода. В итоге запуск перестаёт быть воспроизводимым.

6.1. Формат и модель конфигурации

Выберите удобный формат для команды разработчиков:

  • toml — часто отличный баланс для человеческого чтения и простоты;
  • yaml — гибко, но требует осторожности и совместимости типов;
  • json — предсказуем, но менее удобен для ручного редактирования.

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

6.2. Источники конфигурации и приоритеты

Воспроизводимость достигается ясной и документированной схемой:

  1. базовый конфиг из файла (если указан --config, то он главный);
  2. конфиг по умолчанию (например, ./tool.toml в рабочей директории) — если явный не задан;
  3. окружение (--env) → переключает секцию/набор параметров;
  4. переменные окружения (TOOL_JOBS, TOOL_TOKEN) — только как override (не как источник истины);
  5. CLI-опции → всегда имеют наивысший приоритет.

Так вы сможете объяснить пользователю: «почему взялось это значение».

6.3. Явный merge и следы источников

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

Мини-паттерн: при сборке итоговой конфигурации формируйте структуру value + source.

Пример упрощённой модели:

code
# core/config.py
from dataclasses import dataclass
from typing import Optional, Dict, Any
import os

@dataclass(frozen=True)
class AppConfig:
    env: str
    jobs: int
    output_dir: str
    token: Optional[str]

def load_from_env() -> Dict[str, Any]:
    jobs = os.getenv("TOOL_JOBS")
    return {
        "jobs": int(jobs) if jobs else None,
        "token": os.getenv("TOOL_TOKEN"),
    }

def merge_config(base: Dict[str, Any], overrides: Dict[str, Any]) -> Dict[str, Any]:
    merged = dict(base)
    for k, v in overrides.items():
        if v is not None:
            merged[k] = v
    return merged

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

6.4. Репродуцируемость: фиксируйте окружение и артефакты

Для воспроизводимости важно не только значение конфигурации, но и привязка к контексту:

  • версия инструмента (tool version);
  • версия Python (в идеале — в лог/отчёт);
  • git-ревизия (если это проект);
  • хэши зависимостей или хотя бы lock-файл;
  • таймстемпы/идентификаторы запуска (если операции зависят от внешнего мира).

Практический компромисс: при запуске сохраняйте «снимок» конфигурации и метаданные в файл результата (например, ./.tool-run/run.json).

Пример:

code
# core/audit.py
import json
import os
import platform
from datetime import datetime
from typing import Any, Dict

def write_run_snapshot(path: str, config: Dict[str, Any], extra: Dict[str, Any] | None = None) -> None:
    payload: Dict[str, Any] = {
        "timestamp": datetime.utcnow().isoformat() + "Z",
        "python_version": platform.python_version(),
        "config": config,
    }
    if extra:
        payload["extra"] = extra
    os.makedirs(os.path.dirname(path), exist_ok=True)
    with open(path, "w", encoding="utf-8") as f:
        json.dump(payload, f, ensure_ascii=False, indent=2)

7) Как это связать в целую команду: пример мини-CLI

Ниже — цельная схема, показывающая:

  • параметр --config;
  • команды generate;
  • прогресс;
  • обработка exit codes;
  • snapshot для воспроизводимости.

Чтобы пример оставался читабельным, покажем логику «в связке» без жёсткой привязки к конкретной библиотеке. Но по структуре это переносится на Typer/Click/argparse.

code
# cli/main.py
import sys
import json
from pathlib import Path
from dataclasses import asdict, is_dataclass
from typing import Optional, Dict, Any

from core.errors import CLIUsageError, ConfigError, ExecutionError, TimeoutError
from core.progress import do_work_with_progress
from core.audit import write_run_snapshot

def parse_args(argv) -> Dict[str, Any]:
    # В реальном проекте используйте argparse/Typer.
    # Здесь — упрощённый псевдо-парсер, чтобы показать архитектуру.
    if not argv:
        raise CLIUsageError("Command is required: generate")

    cmd = argv[0]
    if cmd != "generate":
        raise CLIUsageError(f"Unknown command: {cmd}")

    config_path: Optional[str] = None
    env: str = "dev"
    jobs: int = 4
    output_dir: str = "./out"
    progress: str = "auto"

    i = 1
    while i < len(argv):
        if argv[i] == "--config":
            i += 1
            config_path = argv[i]
        elif argv[i] == "--env":
            i += 1
            env = argv[i]
        elif argv[i] == "--jobs":
            i += 1
            jobs = int(argv[i])
        elif argv[i] == "--output-dir":
            i += 1
            output_dir = argv[i]
        elif argv[i] == "--progress":
            i += 1
            progress = argv[i]
        else:
            raise CLIUsageError(f"Unknown option: {argv[i]}")
        i += 1

    if jobs < 1:
        raise CLIUsageError("--jobs must be >= 1")

    return {
        "cmd": cmd,
        "config_path": config_path,
        "env": env,
        "jobs": jobs,
        "output_dir": output_dir,
        "progress": progress,
    }

def load_config_file(path: str) -> Dict[str, Any]:
    p = Path(path)
    if not p.exists():
        raise ConfigError(f"Config file not found: {path}")

    # Пример: читаем json, чтобы не усложнять формат в статье.
    # В реальном проекте чаще используют TOML/YAML.
    try:
        return json.loads(p.read_text(encoding="utf-8"))
    except Exception as e:
        raise ConfigError(f"Invalid config: {e}") from e

def main(argv: list[str]) -> int:
    EXIT_CODES = {
        CLIUsageError: 2,
        ConfigError: 3,
        ExecutionError: 4,
        TimeoutError: 5,
    }

    try:
        args = parse_args(argv)

        config_data: Dict[str, Any] = {}
        if args["config_path"]:
            config_data = load_config_file(args["config_path"])

        # merge config: CLI override всегда сильнее
        merged = dict(config_data)
        merged.update({
            "env": args["env"],
            "jobs": args["jobs"],
            "output_dir": args["output_dir"],
        })

        # Snapshot воспроизводимости
        run_snapshot_path = str(Path(merged["output_dir"]) / ".tool-run" / "run.json")
        write_run_snapshot(run_snapshot_path, merged, extra={"command": args["cmd"]})

        # Прогресс: в реальном CLI решайте "auto/on/off"
        # Здесь просто демонстрируем сам принцип.
        do_work_with_progress(tasks=merged["jobs"])

        return 0

    except Exception as e:
        code = EXIT_CODES.get(type(e), 1)
        print(str(e), file=sys.stderr)
        return code

if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))

Этот пример намеренно упрощён, но показывает важную идею: CLI — оболочка, а не место, где всё перемешано. В реальном проекте парсинг аргументов лучше доверить библиотеке, но схема merge -> snapshot -> run -> exit code остаётся.


8) Typer/Click/argparse: что выбрать и как не ошибиться

Python-библиотек для CLI много, и выбор зависит от требований:

  • argparse: стандартная библиотека, много кода руками, но предсказуемо;
  • Click: зрелая экосистема, удобный API;
  • Typer: строится поверх Click и даёт удобство типизации/документации на основе аннотаций.

Если вы ориентируетесь на «команды, прогресс и конфиги без магии», выбирайте инструмент, который:

  • явно описывает параметры и помогает валидации;
  • не скрывает поведение (как устроены опции/команды);
  • позволяет нормально интегрироваться с логированием и обработкой ошибок.

Тут стоит упомянуть: чтобы разбираться в нюансах Typer глубже (аннотации, схемы команд, обработка параметров), полезно посмотреть материалы из Typer PRO — как способ структурировать знания, а не как «готовое решение».


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

9.1. «exit code всегда 1»

Почти все начинающие проекты со временем упираются в то, что «ничего не работает — вернём 1». Потом приходится отлаживать, потому что вы не знаете класс ошибки.

Решение: задокументируйте коды и маппите их на категории.

9.2. Смешивание stdout и stderr

Когда прогресс/логи пишутся в stdout, пайплайны становятся непредсказуемыми. В лучшем случае — ломается парсинг JSON, в худшем — вы получаете «мусор» в артефактах.

Решение: прогресс и логи — в stderr, данные — в stdout.

9.3. Неочевидный merge конфигурации

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

Решение: фиксируйте приоритет и, по возможности, сохраняйте snapshot.

9.4. Конфиги меняют поведение без версии

Если вы обновили схему конфига или алгоритм, а старые конфиги продолжают «как будто работать», вы получаете тихую деградацию.

Решение: добавляйте версию конфигурации, делайте миграции или хотя бы фиксируйте совместимость.

9.5. Прогресс «всегда включён»

В CI и при перенаправлении потоков прогресс превращается в набор управляющих последовательностей, которые сложно разобрать.

Решение: auto/on/off и проверка TTY.


10) Шаблон структуры репозитория под расширение

Один из лучших индикаторов качества CLI-проекта — как легко вы добавляете новую команду без копипасты и без «всё в main.py».

Ниже — структура, которая обычно хорошо масштабируется.

code
mytool/
  pyproject.toml
  README.md
  LICENSE

  src/
    mytool/
      __init__.py

      cli/
        __init__.py
        main.py          # точка входа
        commands.py      # регистрация команд (если нужно)
        options.py       # общие типы/валидация опций (по желанию)
        runner.py        # обработка ошибок -> exit codes

      core/
        __init__.py
        config.py        # модели и загрузка/merge конфигов
        audit.py         # snapshot запуска
        progress.py      # утилиты для прогресса
        generate.py      # бизнес-логика: generate
        build.py         # бизнес-логика: build (позже)

      integrations/
        __init__.py
        fs.py            # операции с файловой системой
        subprocess.py   # обёртки вызова внешних команд
        net.py           # сеть (если понадобится)

  tests/
    test_config.py
    test_generate.py
    test_cli_exit_codes.py

  docs/
    cli.md               # документация по командам
    config.md            # формат конфигов и приоритеты

Почему это работает

  • core содержит бизнес-логику и почти не зависит от CLI.
  • cli тонкий: парсинг + вызов core + маппинг ошибок/exit codes.
  • integrations изолирует внешние зависимости (файлы/процессы/сеть) — это облегчает тестирование.
  • tests проверяют конфиг и exit code поведение отдельно, не привязываясь к реальному терминалу.

Вывод: CLI как инженерный продукт, а не набор скриптов

Хорошее CLI-приложение для разработчиков — это дисциплина: чёткая архитектура слоёв, понятные команды и опции, стабильные exit codes, прогресс с уважением к логам и воспроизводимость через конфигурацию и snapshot.

Если вы только начинаете или «дорастаете» до расширения инструмента, держите список контрольных вопросов:

  1. Есть ли у каждой команды понятный контракт входных параметров?
  2. Возвращаем ли мы корректные exit codes по классам ошибок?
  3. Не загрязняем ли stdout прогрессом и диагностикой?
  4. Ясна ли схема приоритетов конфигураций и override?
  5. Сохраняем ли мы снимок запуска для воспроизводимости?
  6. Легко ли добавить новую команду без переписывания всего?

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

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

Автор

$ sudo teach IT

Typer PRO

Курс по теме

Typer PRO

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

Открыть курс

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

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

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

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

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

Приложения для iPhone и Apple Watch на SwiftUI

Разработка приложений для iPhone и Apple Watch на SwiftUI: навигация, SwiftData, виджеты, часы, выпуск. Нужен Mac с Xcode 27, сами устройства не нужны.

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

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

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

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

Приложения для macOS на SwiftUI

Разработка приложений для Mac на SwiftUI: окна и меню, Liquid Glass, SwiftData, сеть, выпуск. Нужен Mac с macOS 27 и Xcode 27.

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

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

Как устроен сборщик мусора в CPython: трассировка объектов и циклические ссылки
устроен

Как устроен сборщик мусора в CPython: трассировка объектов и циклические ссылки

Погружаемся в механизм reference counting и cyclic garbage collector — смотрим на исходный код CPython, пишем тесты и выясняем, когда объект действительно удаляется из памяти.

17 июля 2026 г.
580
SQLModel vs SQLAlchemy: что выбрать для Python-проекта с FastAPI
sqlmodel

SQLModel vs SQLAlchemy: что выбрать для Python-проекта с FastAPI

Сравниваем два подхода к работе с базами данных в Python-экосистеме: где SQLModel упрощает жизнь, а где мощность SQLAlchemy незаменима. С примерами моделей, запросов и миграций.

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

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

19 июня 2026 г.
1101
Какой первый проект выбрать новичку, чтобы не бросить обучение
первый

Какой первый проект выбрать новичку, чтобы не бросить обучение

Подберём 5–7 идей под уровень “с нуля”, объясним, что делать по шагам и как довести проект до результата без перегруза. В конце — как оформить мини-портфолио и что показать, даже если проект маленький.

23 сентября 2026 г.
190
FastAPI и фоновые задачи: когда лучше использовать BackgroundTasks, а когда очередь
fastapi

FastAPI и фоновые задачи: когда лучше использовать BackgroundTasks, а когда очередь

Поймём различия между синхронной обработкой, BackgroundTasks и внешними очередями. Разберём idempotency, ретраи и мониторинг фоновых процессов.

22 июля 2026 г.
770
Как выучить программирование самому и бесплатно: с чего начать и в каком порядке
выучить

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

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

21 сентября 2026 г.
290

Комментарии

Пока нет комментариев

Содержание

1) Базовые принципы CLI, которые спасают время1.1. CLI — это контракт, а не удобство для автора1.2. stdout для данных, stderr для диагностики1.3. Exit codes — часть UX для автоматизации2) Архитектура CLI-проекта: команды как единицы ответственности2.1. Минимальный каркас: парсер отдельно от логики3) Проектирование команд и опций без путаницы3.1. Команды как функции: один вход — понятные параметры3.2. Типы параметров и валидация3.3. Уровни сложности: конфиг + переопределения4) Exit codes: как возвращать их правильно и последовательно4.1. Пример матрицы exit codes4.2. Важно: отделяйте ошибки разбора и ошибки выполнения4.3. Реализация: единый механизм завершения5) Прогресс: когда нужен, как сделать аккуратно и не сломать логирование5.1. Общие правила прогресса5.2. Практический подход: Rich как слой визуализации5.3. Сопоставление прогресса с логами6) Конфигурации без магии: источники, приоритеты и воспроизводимость6.1. Формат и модель конфигурации6.2. Источники конфигурации и приоритеты6.3. Явный merge и следы источников6.4. Репродуцируемость: фиксируйте окружение и артефакты7) Как это связать в целую команду: пример мини-CLI8) Typer/Click/argparse: что выбрать и как не ошибиться9) Типичные ошибки в CLI (и как их избежать)9.1. «exit code всегда 1»9.2. Смешивание stdout и stderr9.3. Неочевидный merge конфигурации9.4. Конфиги меняют поведение без версии9.5. Прогресс «всегда включён»10) Шаблон структуры репозитория под расширениеПочему это работаетВывод: CLI как инженерный продукт, а не набор скриптов