$ 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
ГлавнаяБлогTyper в продакшене: конфигурации, автодополнение bash/zsh и сценарии команд с валидацией

Typer в продакшене: конфигурации, автодополнение bash/zsh и сценарии команд с валидацией

$ sudo teach IT
·5 августа 2026 г.·12 мин·68
Typer в продакшене: конфигурации, автодополнение bash/zsh и сценарии команд с валидацией

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

Содержание
Проектирование CLI для продакшена: не «скрипт», а инструментАрхитектура Typer: команды, подкоманды и разделение слоёвПример: единый app и подключение подкомандКонфигурации: загрузка файлов, переменных окружения и приоритетИсточники конфигурацииМодель конфигурации (типизация и дефолты)Загрузка конфигурацииВалидация параметров и дружелюбные ошибкиВалидация через EnumСпециализированные исключения и обработкаПример команды deploy с подкомандами и валидациейЧего избегатьСценарии команд: от «одной функции» к воспроизводимому процессуПример config validate и config printВывод и коды завершения: дисциплина важнее красотыАвтодополнение для bash/zsh: чтобы CLI ощущался «нативным»Добавление команды completionПодводные камни автодополненияТестирование CLI-логики без привязки к терминалуТестирование сервисовИнтеграционные тесты Typer: Capturing stdout/stderrМокирование внешних зависимостейПроверка содержимого выводаПрактические советы по UX: подсказки, форматы и «что делать дальше»Продакшен-аспекты, которые часто забываютЛогирование вместо «print»Версионирование и миграции конфиговСовместимость автодополненияВывод: учебный CLI → инструмент команды

Typer — современный фреймворк для построения CLI на Python: он превращает объявление параметров в команду, умеет подкоманды, интегрируется с типами и предоставляет удобный способ описывать аргументы/опции. Но учебный пример из README редко становится «рабочим инструментом» для продакшена: там обычно нет конфигураций, полноценной валидации, дружелюбных ошибок, автодополнения и, что особенно важно, тестируемой архитектуры.

В этой статье разберём, как довести Typer CLI до уровня утилиты, которую можно безопасно использовать в командах: спроектируем команды и подкоманды, добавим загрузку конфигов (и их приоритет), построим валидацию параметров и понятные сообщения об ошибках, подключим автодополнение для bash/zsh, а также обсудим, как тестировать логику команд без привязки к интерактивному терминалу.


Проектирование CLI для продакшена: не «скрипт», а инструмент

Главная ошибка при переходе от учебных примеров к продакшену — смешивание трёх вещей в одном месте:

  1. Парсинг параметров (Typer/Click-слой).
  2. Бизнес-логика (что именно делает команда).
  3. Взаимодействие с окружением (файлы, сеть, внешние процессы, вывод).

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

Практическая схема:

  • В модулях commands/ лежат функции команд Typer: они принимают строго типизированные аргументы и опции.
  • В модулях services/ живёт бизнес-логика, которая принимает обычные параметры Python и не знает о Typer.
  • В config/ — загрузка конфигов и нормализация.
  • В ui/ — утилиты для сообщений об ошибках/выводе, если нужно.

Всё это не отменяет удобство Typer, но делает проект «склеиваемым», а не «монолитным».


Архитектура Typer: команды, подкоманды и разделение слоёв

Начнём с минимального каркаса. Ниже — структура, которую удобно развивать:

code
mycli/
  __init__.py
  app.py
  commands/
    __init__.py
    deploy.py
    config_cmds.py
  config/
    __init__.py
    loader.py
    models.py
  services/
    __init__.py
    deployer.py
  ui/
    __init__.py
    errors.py

Пример: единый app и подключение подкоманд

code
# app.py
import typer
from commands.deploy import deploy_app
from commands.config_cmds import config_app

app = typer.Typer(
    name="mycli",
    help="Инструмент для деплоя и управления конфигами.",
)

app.add_typer(deploy_app, name="deploy")
app.add_typer(config_app, name="config")

if __name__ == "__main__":
    app()

В Typer Typer() — по сути обёртка над Click. add_typer удобно использовать для логических групп команд: deploy ..., config ..., users ... и т.д.


Конфигурации: загрузка файлов, переменных окружения и приоритет

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

Источники конфигурации

Обычно разумный набор:

  • Конфиг-файл: ~/.mycli/config.toml или ./config.toml (по контексту).
  • Файл, указанный через опцию --config.
  • Переменные окружения MYCLI_*.
  • Аргументы/опции CLI (самый высокий приоритет).

Модель конфигурации (типизация и дефолты)

Пример через pydantic или простые dataclass. Для иллюстрации возьмём dataclass и явную валидацию (можно усилить pydantic).

code
# config/models.py
from dataclasses import dataclass
from typing import Optional

@dataclass(frozen=True)
class AppConfig:
    environment: str               # например: "prod" | "staging"
    api_url: str                   # базовый URL
    token: Optional[str]           # может отсутствовать
    timeout_sec: int = 30

Загрузка конфигурации

Предположим, конфиг в TOML. Можно поддержать YAML/JSON, но TOML удобен для человека и парсится без сюрпризов.

code
# config/loader.py
from __future__ import annotations

import os
from dataclasses import asdict
from pathlib import Path
from typing import Optional

try:
    import tomllib  # py3.11+
except ModuleNotFoundError:  # pragma: no cover
    import tomli as tomllib

from .models import AppConfig


def _default_config_path() -> Path:
    return Path.home() / ".mycli" / "config.toml"


def load_config(path: Optional[str] = None) -> dict:
    config_path = Path(path).expanduser() if path else _default_config_path()
    if not config_path.exists():
        return {}

    with config_path.open("rb") as f:
        data = tomllib.load(f)
    return data


def apply_env_overrides(cfg: dict) -> dict:
    # пример: MYCLI_ENVIRONMENT, MYCLI_API_URL, MYCLI_TOKEN, MYCLI_TIMEOUT_SEC
    mapping = {
        "environment": "MYCLI_ENVIRONMENT",
        "api_url": "MYCLI_API_URL",
        "token": "MYCLI_TOKEN",
        "timeout_sec": "MYCLI_TIMEOUT_SEC",
    }

    out = dict(cfg)
    for key, env_var in mapping.items():
        if env_var in os.environ:
            val: str = os.environ[env_var]
            if key == "timeout_sec":
                out[key] = int(val)
            else:
                out[key] = val
    return out


def merge_cli_overrides(cfg: dict, **overrides) -> dict:
    """
    Клиентские аргументы/опции должны иметь приоритет над файлом и окружением.
    overrides: только те ключи, которые реально заданы CLI.
    """
    out = dict(cfg)
    for k, v in overrides.items():
        if v is not None:
            out[k] = v
    return out


def build_config(
    *,
    config_path: Optional[str] = None,
    environment: Optional[str] = None,
    api_url: Optional[str] = None,
    token: Optional[str] = None,
    timeout_sec: Optional[int] = None,
) -> AppConfig:
    file_cfg = load_config(config_path)
    file_cfg = apply_env_overrides(file_cfg)

    merged = merge_cli_overrides(
        file_cfg,
        environment=environment,
        api_url=api_url,
        token=token,
        timeout_sec=timeout_sec,
    )

    # Нормализация и базовая валидация
    environment_val = merged.get("environment", "staging")
    api_url_val = merged.get("api_url")
    timeout_val = merged.get("timeout_sec", 30)

    if not api_url_val:
        raise ValueError("Не задан api_url. Укажите его в конфиге или передайте --api-url.")

    token_val = merged.get("token")

    return AppConfig(
        environment=environment_val,
        api_url=api_url_val,
        token=token_val,
        timeout_sec=int(timeout_val),
    )

Ключевой момент: build_config получает параметры CLI (все опциональные), грузит конфиг и окружение, затем применяет переопределения CLI. Это обеспечивает стабильный приоритет.


Валидация параметров и дружелюбные ошибки

Typer умеет многое автоматически благодаря аннотациям типов, но для продакшена почти всегда нужна дополнительная валидация:

  • Проверки допустимых значений (enum).
  • Валидация форматов (URL, пути, токены по длине/маске).
  • Согласованность параметров (например: --dry-run несовместим с --apply, или токен обязателен в prod).
  • Понятные сообщения об ошибках без стека трейсинга.

Валидация через Enum

code
# commands/deploy.py (фрагмент)
import typer
from typing import Optional
from enum import Enum

class Environment(str, Enum):
    prod = "prod"
    staging = "staging"
    dev = "dev"

Typer отрисует подсказки и ограничит значения.

Специализированные исключения и обработка

Лучше ловить предсказуемые ошибки отдельно от «неожиданных». Например:

code
# ui/errors.py
class CLIValidationError(Exception):
    """Ошибки в параметрах пользователя (предсказуемые, без стека)."""

Используем её в командах.

Пример команды deploy с подкомандами и валидацией

Допустим, у нас есть deploy run и deploy rollback. run требует образ и окружение, а rollback требует идентификатор релиза.

code
# commands/deploy.py
from __future__ import annotations

import typer
from typing import Optional
from enum import Enum

from config.loader import build_config
from services.deployer import Deployer
from ui.errors import CLIValidationError

deploy_app = typer.Typer(help="Команды деплоя.")

class Environment(str, Enum):
    prod = "prod"
    staging = "staging"
    dev = "dev"


def _validate_timeout(timeout_sec: int) -> int:
    if timeout_sec < 1 or timeout_sec > 300:
        raise CLIValidationError("--timeout-sec должен быть в диапазоне 1..300.")
    return timeout_sec


@deploy_app.command("run")
def run(
    image: str = typer.Option(..., "--image", help="Docker image (например: org/app:1.2.3)."),
    env: Environment = typer.Option(Environment.staging, "--env", help="Среда деплоя."),
    config_path: Optional[str] = typer.Option(None, "--config", help="Путь к config.toml."),
    api_url: Optional[str] = typer.Option(None, "--api-url", help="Переопределить api_url из конфигов."),
    token: Optional[str] = typer.Option(None, "--token", help="Токен для API."),
    timeout_sec: int = typer.Option(30, "--timeout-sec", show_default=True),
    dry_run: bool = typer.Option(False, "--dry-run", help="Не выполнять, только показать план."),
):
    """
    Запустить деплой.
    """
    try:
        timeout_sec = _validate_timeout(timeout_sec)

        cfg = build_config(
            config_path=config_path,
            environment=env.value,
            api_url=api_url,
            token=token,
            timeout_sec=timeout_sec,
        )

        # Дополнительная бизнес-валидация
        if cfg.environment == "prod" and not cfg.token:
            raise CLIValidationError("Для prod нужен токен. Укажите --token или MYCLI_TOKEN.")

        Deployer(cfg).deploy(image=image, dry_run=dry_run)

    except CLIValidationError as e:
        typer.secho(f"Ошибка: {e}", fg=typer.colors.RED, err=True)
        raise typer.Exit(code=2)

Тут важны детали:

  • Валидация таймаута и бизнес-условия (prod без токена) — отдельными проверками.
  • CLIValidationError превращается в аккуратное сообщение.
  • Мы не выводим трассировку — только пользовательскую причину.

Чего избегать

  • Не ловите Exception «на всякий случай» и не превращайте её в одинаковую ошибку. Иначе вы скрываете реальные проблемы.
  • Не делайте валидацию «внутри» сервиса без контекста. Пользователю полезнее знать, что именно из его параметров не прошло валидацию.

Сценарии команд: от «одной функции» к воспроизводимому процессу

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

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

Пример config validate и config print

code
# commands/config_cmds.py
from __future__ import annotations

import typer
from typing import Optional

from config.loader import build_config
from ui.errors import CLIValidationError

config_app = typer.Typer(help="Управление конфигом.")


@config_app.command("validate")
def validate(
    config_path: Optional[str] = typer.Option(None, "--config", help="Путь к config.toml."),
    api_url: Optional[str] = typer.Option(None, "--api-url"),
    token: Optional[str] = typer.Option(None, "--token"),
    env: Optional[str] = typer.Option("staging", "--env"),
):
    """
    Проверить, что конфигурация валидна.
    """
    try:
        cfg = build_config(
            config_path=config_path,
            environment=env,
            api_url=api_url,
            token=token,
            timeout_sec=None,
        )
        typer.echo("Конфигурация валидна.")
        typer.echo(f"environment={cfg.environment}")
        typer.echo(f"api_url={cfg.api_url}")
    except ValueError as e:
        raise CLIValidationError(str(e))
    except CLIValidationError as e:
        typer.secho(f"Ошибка: {e}", fg=typer.colors.RED, err=True)
        raise typer.Exit(code=2)


@config_app.command("print")
def print_config(
    config_path: Optional[str] = typer.Option(None, "--config"),
    env: Optional[str] = typer.Option(None, "--env"),
    api_url: Optional[str] = typer.Option(None, "--api-url"),
    token: Optional[str] = typer.Option(None, "--token"),
):
    """
    Показать эффективные параметры (после приоритетов).
    """
    cfg = build_config(
        config_path=config_path,
        environment=env,
        api_url=api_url,
        token=token,
        timeout_sec=None,
    )
    typer.echo(cfg)  # в проде лучше форматировать аккуратно

Такой CLI становится «инструментом управления», а не набором разрозненных команд.


Вывод и коды завершения: дисциплина важнее красоты

В продакшене важны:

  • коды возврата (exit codes) для автоматизации;
  • предсказуемость формата сообщений;
  • отсутствие «лишнего» вывода в stdout при ошибках (ошибки — в stderr).

Typer/Click позволяют управлять этим через typer.secho(..., err=True) и typer.Exit(code=...).

Рекомендуемая схема:

  • 0 — успех;
  • 2 — ошибки параметров/валидации;
  • 1 — прочие ошибки (например, сетевые/файловые), можно расширить подкатегории.

Автодополнение для bash/zsh: чтобы CLI ощущался «нативным»

Typer поддерживает генерацию скриптов автодополнения (через Click). Это обычно делается командой вида:

code
mycli --help  # посмотреть, есть ли командa автодополнения

Но на практике проще использовать встроенные механизмы Click: app генерирует bash/zsh completion.

В Typer можно добавить команды completion. Часто используют подход:

  • mycli completion bash > /etc/bash_completion.d/mycli
  • mycli completion zsh > ...

Покажем пример, который обычно работает в экосистеме Click/Typer.

Добавление команды completion

code
# app.py
import typer
from commands.deploy import deploy_app
from commands.config_cmds import config_app

app = typer.Typer(name="mycli")

app.add_typer(deploy_app, name="deploy")
app.add_typer(config_app, name="config")

# Click/ Typer обычно уже имеют поддержку генерации completion,
# но в некоторых версиях её нужно явно прокинуть через command.
# Если у вас есть встроенная поддержка, эта часть не обязательна.
# Для конкретной версии Typer/Click стоит проверить доступность:
# `python -m mycli --help` / `mycli completion --help`.
if __name__ == "__main__":
    app()

Дальше по документации CLI обычно используют:

code
mycli completion bash > /etc/bash_completion.d/mycli
mycli completion zsh > "${fpath[1]}/_mycli"

Подводные камни автодополнения

  • Автодополнение зависит от имён команд и опций. Если вы переименуете опции — нужно обновлять скрипт (или хотя бы документировать, как его регенерировать).
  • Корректные типы (enum, ограниченные значения) улучшают подсказки: например, --env будет подсказывать только prod/staging/dev.
  • Если у вас динамические значения (например, список окружений приходит из сети), автодополнение не «угадает» их. В таком случае обычно делают компромисс: автодополнение для фиксированных параметров + fallback валидация на runtime.

Тестирование CLI-логики без привязки к терминалу

Одна из причин, почему Typer любим: команды легко тестировать. Но важно тестировать не «терминал», а бизнес-логику и контракты интерфейса.

Есть два уровня тестов:

  1. Юнит-тесты сервисов (Deployer.deploy(...)) — полностью без Typer.
  2. Интеграционные тесты CLI — проверяем, что команды правильно реагируют на входные аргументы/опции, а ошибки конвертируются в ожидаемый вывод и exit code.

Тестирование сервисов

code
# services/deployer.py
from dataclasses import dataclass

@dataclass
class Deployer:
    cfg: object

    def deploy(self, image: str, dry_run: bool = False) -> None:
        if dry_run:
            # логика построения плана
            return
        # условно: отправка запроса в API
        return

Юнит-тест:

code
# tests/test_deployer.py
import pytest
from services.deployer import Deployer
from config.models import AppConfig

def test_deploy_dry_run_does_not_require_token():
    cfg = AppConfig(environment="prod", api_url="https://example.com", token=None, timeout_sec=30)
    d = Deployer(cfg)
    # В сервисе можно не валидировать prod/token, это делается на уровне CLI.
    d.deploy(image="org/app:1.0.0", dry_run=True)

Если валидировать prod/token на уровне CLI (как в примере), то сервис можно тестировать проще: он делает «работу», а не проверяет параметры пользователя.

Интеграционные тесты Typer: Capturing stdout/stderr

Typer базируется на Click и предлагает тестирование через CliRunner (из typer.testing). Это не «интерактивный терминал», а воспроизводимый запуск команды.

Пример:

code
# tests/test_cli.py
from typer.testing import CliRunner
import pytest

from app import app

runner = CliRunner()

def test_deploy_run_requires_token_in_prod():
    result = runner.invoke(
        app,
        ["deploy", "run", "--image", "org/app:1.0.0", "--env", "prod"],
    )
    assert result.exit_code == 2
    assert "Для prod нужен токен" in result.stderr

def test_deploy_run_dry_run_in_prod_without_token():
    result = runner.invoke(
        app,
        ["deploy", "run", "--image", "org/app:1.0.0", "--env", "prod", "--dry-run"],
        env={"MYCLI_API_URL": "https://example.com"}  # полезно подставить обязательные параметры
    )
    assert result.exit_code == 0

Тут важно:

  • runner.invoke запускает CLI как функцию, без терминала.
  • Можно передавать env={...} — это решает проблему «как тестировать без реального окружения».
  • Сервис не дергает сеть в тестах: его лучше мокать или оставлять только проверку валидации.

Мокирование внешних зависимостей

Если Deployer.deploy делает запросы, в интеграционных тестах лучше мокнуть. Pytest + monkeypatch:

code
# tests/test_cli_mock.py
from typer.testing import CliRunner
from app import app

runner = CliRunner()

def test_deploy_run_calls_service(monkeypatch):
    calls = {}

    def fake_deploy(self, image: str, dry_run: bool = False):
        calls["image"] = image
        calls["dry_run"] = dry_run

    # Подменяем метод
    monkeypatch.setattr("services.deployer.Deployer.deploy", fake_deploy)

    result = runner.invoke(
        app,
        ["deploy", "run", "--image", "org/app:2.0.0", "--env", "staging", "--token", "abc"],
        env={"MYCLI_API_URL": "https://example.com"},
    )

    assert result.exit_code == 0
    assert calls["image"] == "org/app:2.0.0"
    assert calls["dry_run"] is False

Если используете monkeypatch по месту, убедитесь, что путь к объекту корректен (это зависит от того, как импортирован Deployer в commands/deploy.py).

Проверка содержимого вывода

Для дружелюбных ошибок полезно проверять:

  • exit_code,
  • фрагмент текста в stderr,
  • отсутствие «лишнего» текста в stdout.

Это делает поведение стабильным при рефакторинге.


Практические советы по UX: подсказки, форматы и «что делать дальше»

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

Минимальный UX-чеклист:

  • Ошибка должна указывать: что именно не так.
  • Сообщение должно говорить: что сделать (указать параметр, исправить диапазон, предоставить токен).
  • Не дублируйте то, что Typer уже пишет в usage — лучше добавьте контекст.

Пример дружелюбной ошибки (как в --timeout-sec диапазоне):

Ошибка: --timeout-sec должен быть в диапазоне 1..300.

А вот менее полезное:

Ошибка: validation failed


Продакшен-аспекты, которые часто забывают

Логирование вместо «print»

В реальном инструменте полезно иметь уровни логов: --verbose/--quiet и вывод в файл при необходимости. Typer не заставляет вас использовать logging, но лучше включить стандартную библиотеку logging.

Версионирование и миграции конфигов

Если формат конфига меняется, вы столкнётесь с «почему оно не работает у пользователя». Хорошая практика:

  • config.version в файле;
  • миграция или понятное сообщение «обновите конфиг».

Совместимость автодополнения

Если команда распространяется в организации, где shell completion включают один раз, то изменения в CLI — это изменения в автодополнении. Документируйте команду регенерации в репозитории.


Вывод: учебный CLI → инструмент команды

Чтобы Typer CLI стал пригодным для продакшена, мало красиво описать параметры. Нужно:

  • разделить парсинг и бизнес-логику;
  • реализовать внятные конфигурации с предсказуемым приоритетом (CLI > env > файл);
  • добавить валидацию с исключениями уровня «ошибка пользователя» и дружелюбными сообщениями;
  • построить сценарии через подкоманды;
  • подключить автодополнение bash/zsh для удобства ежедневной работы;
  • тестировать логику без терминальной зависимости через CliRunner и юнит-тесты сервисов.

Если вы хотите глубже разобраться в «практической стороне» Typer — от композиции команд до тонкостей настройки и тестирования — в качестве следующего шага можно посмотреть курс Typer PRO. Он помогает структурировать подход и закрепить лучшие практики на реальных кейсах, но ключевые принципы из этой статьи уже можно применить прямо сейчас к вашему проекту.

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

Автор

$ sudo teach IT

Typer PRO

Курс по теме

Typer PRO

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

Открыть курс

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

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

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

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

Перейти к курсу
Solidity DLC - поймай, если сможешь

Solidity DLC - поймай, если сможешь

Аудит смарт-контрактов на реальных взломах. The DAO, Parity, Wormhole, Ronin, Euler и ещё 7 разборов: что пошло не так, сколько потеряли и что бы спасло. Атаки и защиты на Foundry, план входа в профессию аудитора.

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

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

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

Перейти к курсу
Solidity pro - всё или ничего

Solidity pro - всё или ничего

Как пишут контракты, которые работают в основной сети. Наследование и OpenZeppelin, паттерны продакшена, оптимизация газа, ABI, delegatecall и подписи. 7 модулей, 33 урока, а в финале собственное dApp: мини-DAO с токеном, голосованием и казной.

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

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

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

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

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

15 сентября 2026 г.
1400
Пишем корректные bash-скрипты: обработка ошибок, строгие режимы и безопасные переменные
пишем

Пишем корректные bash-скрипты: обработка ошибок, строгие режимы и безопасные переменные

Покажем best practices: set -euo pipefail, ловля ошибок, корректное quoting и проверка входных параметров. Разберём мини-проект с устойчивым поведением.

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

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

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

21 сентября 2026 г.
2080
Как выбрать первый сайт-проект новичку: что сделать за вечер и расширять дальше
выбрать

Как выбрать первый сайт-проект новичку: что сделать за вечер и расширять дальше

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

4 ноября 2026 г.
80
Cargo для продакшена: workspaces, фичи и стратегия версий зависимостей
cargo

Cargo для продакшена: workspaces, фичи и стратегия версий зависимостей

Разберём, как организовать Rust workspace, управлять optional features, фиксировать версии библиотек и уменьшать “дрейф” зависимостей при командной разработке и CI.

18 сентября 2026 г.
2070
Как начать с HTML и сделать первую страницу: пошагово и без сложных слов
html

Как начать с HTML и сделать первую страницу: пошагово и без сложных слов

Разберём, что нужно новичку, чтобы за 1–2 вечера собрать свою первую веб-страницу: структура HTML, базовые теги, ссылки и картинки, плюс простой чек-лист, как понять, что страница работает правильно.

2 ноября 2026 г.
140

Комментарии

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

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

Содержание

Проектирование CLI для продакшена: не «скрипт», а инструментАрхитектура Typer: команды, подкоманды и разделение слоёвПример: единый app и подключение подкомандКонфигурации: загрузка файлов, переменных окружения и приоритетИсточники конфигурацииМодель конфигурации (типизация и дефолты)Загрузка конфигурацииВалидация параметров и дружелюбные ошибкиВалидация через EnumСпециализированные исключения и обработкаПример команды deploy с подкомандами и валидациейЧего избегатьСценарии команд: от «одной функции» к воспроизводимому процессуПример config validate и config printВывод и коды завершения: дисциплина важнее красотыАвтодополнение для bash/zsh: чтобы CLI ощущался «нативным»Добавление команды completionПодводные камни автодополненияТестирование CLI-логики без привязки к терминалуТестирование сервисовИнтеграционные тесты Typer: Capturing stdout/stderrМокирование внешних зависимостейПроверка содержимого выводаПрактические советы по UX: подсказки, форматы и «что делать дальше»Продакшен-аспекты, которые часто забываютЛогирование вместо «print»Версионирование и миграции конфиговСовместимость автодополненияВывод: учебный CLI → инструмент команды