architect/standards/5-format/format-code.md

type: standard
aspect: format
title: "Стандарт стиля кода"
version: 1.0.0
date: 2026-02-19
status: active


Стандарт стиля кода

Правила форматирования и стиля для Python, JavaScript, TypeScript, Bash.


ОБЩИЕ ПРИНЦИПЫ

Читабельность > Краткость

# ✅ Правильно (читабельно)
user_count = get_active_users().count()

# ❌ Неправильно (слишком кратко)
n = db.u.a().c()

Явное > Неявное

# ✅ Правильно (явно)
from datetime import datetime
now = datetime.now()

# ❌ Неправильно (неявно)
from datetime import *
now = now()

Консистентность > Индивидуальность

Следуем стандартам языка:
- Python → PEP 8
- JavaScript → Airbnb Style Guide
- TypeScript → TypeScript Style Guide
- Bash → Google Shell Style Guide


PYTHON

Базовые правила (PEP 8)

Отступы:
- 4 пробела (не табы)
- Никогда не смешивать табы и пробелы

Длина строки:
- Максимум 88 символов (Black formatter)
- Для docstring и комментариев: 72 символа

Пустые строки:
- 2 пустые строки между функциями верхнего уровня
- 2 пустые строки между классами
- 1 пустая строка между методами класса

Кавычки:
- Двойные " для строк (не одинарные)
- Docstring всегда тройные двойные """

Именование

Переменные и функции: snake_case

user_name = "Ivan"
order_count = 10

def get_user_by_id(user_id):
    pass

Классы: PascalCase

class UserManager:
    pass

class OrderProcessor:
    pass

Константы: UPPER_SNAKE_CASE

MAX_RETRIES = 3
API_TIMEOUT = 30
DATABASE_URL = "postgresql://..."

Приватные: префикс _

class User:
    def __init__(self):
        self._password = None  # приватный атрибут

    def _hash_password(self):  # приватный метод
        pass

Магические методы: __double_underscore__

def __init__(self):
    pass

def __str__(self):
    pass

Импорты

Порядок:

# 1. Стандартная библиотека
import os
import sys
from datetime import datetime

# 2. Сторонние библиотеки
import requests
from flask import Flask

# 3. Локальные модули
from library.connectors.api.ozon import OzonClient
from projects.pirotehnika.data import Product

Правила:
- Каждый импорт на отдельной строке
- Сортировка по алфавиту внутри группы
- Абсолютные импорты (не относительные для библиотек)

Правильно:

import os
import sys

Неправильно:

import os, sys  # не на одной строке

Типизация (Type Hints)

Обязательно для всех функций:

from typing import List, Dict, Optional

def get_users(limit: int = 10) -> List[Dict[str, str]]:
    """Получить список пользователей."""
    return []

def find_user(user_id: int) -> Optional[Dict]:
    """Найти пользователя по ID."""
    return None

Для переменных (если тип неочевиден):

users: List[User] = []
config: Dict[str, Any] = {}
user: Optional[User] = None

Docstrings

Формат: Google Style

def sync_orders(
    api_key: str,
    date_from: str,
    limit: int = 100
) -> Dict[str, int]:
    """
    Синхронизировать заказы из OZON API.

    Args:
        api_key: API ключ OZON
        date_from: Дата начала в формате YYYY-MM-DD
        limit: Максимальное количество заказов (по умолчанию 100)

    Returns:
        Словарь со статистикой:
        - total: общее количество заказов
        - new: новые заказы
        - updated: обновлённые заказы

    Raises:
        ValueError: Если date_from в неправильном формате
        APIError: Если API вернул ошибку

    Examples:
        >>> result = sync_orders("key123", "2026-02-01")
        >>> print(result['total'])
        150
    """
    pass

Для модулей:

"""
Модуль для работы с OZON API.

Основные компоненты:
- OzonClient: клиент для взаимодействия с API
- sync_orders: синхронизация заказов
- sync_products: синхронизация товаров
"""

Форматирование кода

Используем Black:

# Установка
pip install black

# Форматирование
black file.py
black directory/

# Проверка (без изменений)
black --check file.py

Настройка (pyproject.toml):

[tool.black]
line-length = 88
target-version = ['py39', 'py310', 'py311']
include = '\.pyi?$'

Проверка кода

Линтер: ruff (быстрее pylint/flake8)

# Установка
pip install ruff

# Проверка
ruff check .

# Автофикс
ruff check --fix .

Настройка (.ruff.toml):

line-length = 88
target-version = "py39"

[lint]
select = ["E", "F", "W", "I", "N"]
ignore = ["E501"]  # line too long (black handles)

JAVASCRIPT / TYPESCRIPT

Базовые правила

Отступы:
- 2 пробела (не 4, не табы)

Длина строки:
- Максимум 100 символов

Точка с запятой:
- Обязательна (не полагаемся на ASI)

Кавычки:
- Одинарные ' для строк
- **Обратные ` для template strings

Именование

Переменные и функции: camelCase

const userName = 'Ivan';
const orderCount = 10;

function getUserById(userId) {
  // ...
}

Классы: PascalCase

class UserManager {
  // ...
}

class OrderProcessor {
  // ...
}

Константы: UPPER_SNAKE_CASE

const MAX_RETRIES = 3;
const API_TIMEOUT = 30;

Приватные поля (TypeScript/ES2022): префикс #

class User {
  #password;

  #hashPassword() {
    // ...
  }
}

Современный синтаксис

Используем:

// ✅ const/let (не var)
const users = [];
let count = 0;

// ✅ Arrow functions
const double = (x) => x * 2;

// ✅ Template strings
const message = `Hello, ${userName}!`;

// ✅ Destructuring
const { name, age } = user;
const [first, second] = items;

// ✅ Spread operator
const newArray = [...oldArray, newItem];
const newObject = { ...oldObject, newField: value };

// ✅ Optional chaining
const city = user?.address?.city;

// ✅ Nullish coalescing
const name = user.name ?? 'Guest';

НЕ используем:

// ❌ var
var count = 0;

// ❌ function вместо arrow (для коротких функций)
const double = function(x) { return x * 2; };

// ❌ Конкатенация строк
const message = 'Hello, ' + userName + '!';

TypeScript типизация

Обязательно для всех функций:

function getUsers(limit: number = 10): User[] {
  return [];
}

function findUser(userId: number): User | null {
  return null;
}

async function syncOrders(apiKey: string): Promise<SyncResult> {
  // ...
}

Интерфейсы вместо типов (где возможно):

// ✅ Правильно
interface User {
  id: number;
  name: string;
  email: string;
}

// ⚠️ Использовать только для union/intersection
type Status = 'pending' | 'active' | 'completed';

Форматирование (Prettier)

Настройка (.prettierrc):

{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "printWidth": 100,
  "trailingComma": "es5",
  "arrowParens": "always"
}

Линтер (ESLint)

Настройка (.eslintrc.js):

module.exports = {
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier'
  ],
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint'],
  rules: {
    'no-console': 'warn',
    '@typescript-eslint/no-unused-vars': 'error',
    '@typescript-eslint/explicit-function-return-type': 'warn'
  }
};

BASH

Базовые правила

Отступы:
- 2 пробела (не табы)

Длина строки:
- Максимум 80 символов
- Длинные команды разбивать с \

Shebang:

#!/bin/bash

Именование

Переменные: snake_case

user_name="Ivan"
order_count=10

Константы: UPPER_SNAKE_CASE

readonly MAX_RETRIES=3
readonly API_TIMEOUT=30

Функции: snake_case

get_user_by_id() {
  local user_id=$1
  # ...
}

Кавычки

Всегда кавычки для переменных:

# ✅ Правильно
echo "User: $user_name"
cp "$source_file" "$dest_file"

# ❌ Неправильно (проблемы с пробелами)
echo User: $user_name
cp $source_file $dest_file

Двойные " vs одинарные ':

# Двойные — подстановка переменных
echo "Hello, $name"  # Hello, Ivan

# Одинарные — литерал
echo 'Hello, $name'  # Hello, $name

Условия

Используем [[ ]] (не [ ]):

# ✅ Правильно
if [[ -f "$file" ]]; then
  echo "File exists"
fi

if [[ "$status" == "active" ]]; then
  echo "Active"
fi

# ❌ Неправильно
if [ -f $file ]; then  # проблемы с пробелами
  echo "File exists"
fi

Функции

Объявление:

# ✅ Правильно
function backup_database() {
  local db_name=$1
  local backup_dir=$2

  # Проверка аргументов
  if [[ -z "$db_name" ]]; then
    echo "Error: db_name required"
    return 1
  fi

  # Логика бэкапа
  echo "Backing up $db_name to $backup_dir"
}

# ❌ Неправильно (без local)
backup_database() {
  db_name=$1  # глобальная переменная!
}

Вызов:

backup_database "mydb" "/backups"

Обработка ошибок

set -e / set -u:

#!/bin/bash
set -e  # остановиться при ошибке
set -u  # ошибка при неопределённой переменной
set -o pipefail  # ошибка в pipeline

# Альтернатива: одной строкой
set -euo pipefail

Проверка кода возврата:

# ✅ Правильно
if command; then
  echo "Success"
else
  echo "Failed"
  exit 1
fi

# Или
command || { echo "Failed"; exit 1; }

Комментарии и документация

Заголовок скрипта:

#!/bin/bash
#
# backup.sh — резервное копирование базы данных
#
# Использование:
#   ./backup.sh DATABASE_NAME BACKUP_DIR
#
# Примеры:
#   ./backup.sh mydb /backups
#
# Автор: architect
# Дата: 2026-02-19

Комментарии функций:

# Создать резервную копию базы данных
#
# Args:
#   $1 - имя базы данных
#   $2 - директория для бэкапа
#
# Returns:
#   0 если успех, 1 если ошибка
function backup_database() {
  # ...
}

Проверка (ShellCheck)

# Установка
apt-get install shellcheck

# Проверка
shellcheck script.sh

ИНСТРУМЕНТЫ

Python

Инструмент Назначение Команда
black Форматирование black .
ruff Линтер + автофикс ruff check --fix .
mypy Проверка типов mypy .
isort Сортировка импортов isort .

JavaScript/TypeScript

Инструмент Назначение Команда
prettier Форматирование prettier --write .
eslint Линтер eslint --fix .
tsc Проверка типов (TS) tsc --noEmit

Bash

Инструмент Назначение Команда
shellcheck Линтер shellcheck script.sh
shfmt Форматирование shfmt -w script.sh

PRE-COMMIT HOOKS

Автоматическая проверка перед коммитом:

.git/hooks/pre-commit:

#!/bin/bash
set -e

echo "Running code checks..."

# Python
black --check .
ruff check .

# JavaScript/TypeScript
prettier --check .
eslint .

# Bash
find . -name "*.sh" -exec shellcheck {} \;

echo "✓ All checks passed"
chmod +x .git/hooks/pre-commit

ПРИМЕРЫ

Python — полный файл

"""
Модуль для синхронизации заказов с OZON API.

Основные функции:
- sync_orders: синхронизация всех заказов
- sync_order: синхронизация одного заказа
"""

import logging
from datetime import datetime
from typing import Dict, List, Optional

import requests

from library.connectors.api.ozon import OzonClient


logger = logging.getLogger(__name__)

API_TIMEOUT = 30
MAX_RETRIES = 3


class OrderSyncError(Exception):
    """Ошибка синхронизации заказов."""
    pass


def sync_orders(
    api_key: str,
    date_from: str,
    limit: int = 100
) -> Dict[str, int]:
    """
    Синхронизировать заказы из OZON API.

    Args:
        api_key: API ключ OZON
        date_from: Дата начала в формате YYYY-MM-DD
        limit: Максимальное количество заказов

    Returns:
        Словарь со статистикой синхронизации

    Raises:
        OrderSyncError: При ошибке синхронизации
    """
    logger.info(f"Syncing orders from {date_from}, limit={limit}")

    client = OzonClient(api_key=api_key, timeout=API_TIMEOUT)

    try:
        orders = client.get_orders(date_from=date_from, limit=limit)

        total = len(orders)
        new_count = 0

        for order in orders:
            if _is_new_order(order):
                _save_order(order)
                new_count += 1

        logger.info(f"Synced {total} orders, {new_count} new")

        return {
            'total': total,
            'new': new_count,
            'updated': total - new_count
        }

    except requests.HTTPError as e:
        logger.error(f"API error: {e}")
        raise OrderSyncError(f"Failed to sync orders: {e}")


def _is_new_order(order: Dict) -> bool:
    """Проверить является ли заказ новым."""
    # Логика проверки
    return True


def _save_order(order: Dict) -> None:
    """Сохранить заказ в базу."""
    # Логика сохранения
    pass

TypeScript — полный файл

/**
 * Модуль для работы с пользователями.
 */

interface User {
  id: number;
  name: string;
  email: string;
  createdAt: Date;
}

interface CreateUserParams {
  name: string;
  email: string;
}

/**
 * Получить пользователя по ID.
 */
async function getUser(userId: number): Promise<User | null> {
  const response = await fetch(`/api/users/${userId}`);

  if (!response.ok) {
    return null;
  }

  return response.json();
}

/**
 * Создать нового пользователя.
 */
async function createUser(params: CreateUserParams): Promise<User> {
  const response = await fetch('/api/users', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(params),
  });

  if (!response.ok) {
    throw new Error('Failed to create user');
  }

  return response.json();
}

export { User, getUser, createUser };

CHANGELOG

2026-02-19 — v1.0.0


Версия: 1.0.0
Статус: active
Владелец: architect