architect/_archive/2026-04-11/standards-old/policy/policy-code-compatibility.md

type: standard
aspect: policy
title: "Совместимость кода — гарантия исполнения"
version: 1.0.0
date: 2026-04-05
status: active
languages: [Python, JavaScript, TypeScript, Go, Bash]


Совместимость кода — гарантия исполнения

Цель: 100% исполнение кода платформы независимо от версий, диалектов, интерпретаторов и среды выполнения.

Область: весь код в library/, infra/, projects/, system/, tools/.


ПРИНЦИП

КОД ПИШЕТСЯ ПОД МИНИМУМ, А НЕ ПОД ТЕКУЩУЮ МАШИНУ.

Каждый файл декларирует свои требования.
Каждый автор знает границы совместимости.
Каждый CI проверяет соответствие.

МАТРИЦА СОВМЕСТИМОСТИ ПЛАТФОРМЫ

Язык Минимальная версия Целевая версия Интерпретатор Платформа
Python 3.10 3.12 CPython only Linux x86_64
JavaScript Node.js 20 LTS Node.js 22 LTS Node.js only Linux x86_64
TypeScript 5.0 5.4+ tsc → Node.js Linux x86_64
Go 1.21 1.22+ gc (стандартный) Linux x86_64 (cross: darwin, windows)
Bash 5.0 5.1+ bash only Linux x86_64

Обоснование минимумов:
- Python 3.10 — сервер dev-pro-eu (Ubuntu 22.04), match/case, X | Y union types
- Node.js 20 — текущий LTS, поддержка до 2026-04
- Go 1.21 — slices/maps пакеты, min/max builtins
- Bash 5.0 — Ubuntu 20.04+, ассоциативные массивы, nameref


1. PYTHON

1.1. Таблица рисков

Риск Причина Проявление Решение
list[int] vs List[int] Синтаксис generic типов изменился в 3.9 TypeError на 3.8 Использовать list[int] (мин. 3.10)
X \| Y union syntax Появился в 3.10 SyntaxError на 3.9 Безопасно: минимум = 3.10
match/case Появился в 3.10 SyntaxError на 3.9 Безопасно: минимум = 3.10
type X = ... Появился в 3.12 SyntaxError на 3.10-3.11 ЗАПРЕЩЕНО — использовать TypeAlias
f"{x:{fmt}}" вложенные Полноценно в 3.12 SyntaxError на 3.10-3.11 ЗАПРЕЩЕНО — разделить на две строки
ExceptionGroup Появился в 3.11 NameError на 3.10 ЗАПРЕЩЕНО без проверки версии
tomllib Появился в 3.11 ModuleNotFoundError на 3.10 Использовать tomli (backport) для 3.10
encoding по умолчанию Windows=cp1252, Linux=utf-8 Мусор в строках Всегда указывать encoding="utf-8"
dict порядок Гарантирован с 3.7 Безопасно
asyncio.TaskGroup Появился в 3.11 AttributeError на 3.10 Использовать asyncio.gather()
PyPy / Jython Другие VM C-extensions падают НЕ ПОДДЕРЖИВАЕМ

1.2. ОБЯЗАТЕЛЬНЫЕ правила (MUST)

M-PY-01. Shebang:

#!/usr/bin/env python3

Не #!/usr/bin/python3 — путь различается на разных дистрибутивах.

M-PY-02. Encoding в файловых операциях:

# ПРАВИЛЬНО
with open("file.txt", encoding="utf-8") as f:
    data = f.read()

# ПРАВИЛЬНО — subprocess
result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8")

M-PY-03. Типы — встроенные generics:

# ПРАВИЛЬНО (3.10+)
def process(items: list[str], cache: dict[str, int]) -> tuple[bool, str | None]:
    ...

# ДОПУСТИМО — для сложных типов
from typing import TypeVar, Generic, Protocol, TypeAlias

M-PY-04. Декларация совместимости в pyproject.toml:

[project]
requires-python = ">=3.10"

M-PY-05. Пути — только pathlib или os.path:

from pathlib import Path

config = Path(__file__).parent / "config.json"
# НЕ: config = __file__.replace("module.py", "config.json")

M-PY-06. Строки — только f-strings, без вложенных выражений:

# ПРАВИЛЬНО
name = f"Товар: {product.name}"
formatted = f"Цена: {price:,.2f}"

# ЗАПРЕЩЕНО (3.12+ only)
formatted = f"Цена: {price:{width}.{precision}f}"

# ПРАВИЛЬНО — альтернатива
fmt = f"{width}.{precision}f"
formatted = f"Цена: {format(price, fmt)}"

M-PY-07. Import — абсолютные пути для library/:

# ПРАВИЛЬНО
from library.primitives.types import Money
from library.functions.format.money import format_money

# ЗАПРЕЩЕНО для library/*
from ..primitives.types import Money

M-PY-08. Subprocess — список аргументов, не строка:

# ПРАВИЛЬНО
subprocess.run(["ls", "-la", path], check=True)

# ЗАПРЕЩЕНО (shell injection)
subprocess.run(f"ls -la {path}", shell=True)

1.3. ЗАПРЕЩЁННЫЕ паттерны (NEVER)

Паттерн Почему запрещён Альтернатива
type X = int \| str Python 3.12+ only X: TypeAlias = int \| str
f"{val:{spec}}" вложенные f-strings 3.12+ only format(val, spec)
ExceptionGroup / except* 3.11+ only try/except обычный
asyncio.TaskGroup 3.11+ only asyncio.gather()
tomllib без fallback 3.11+ only try: import tomllib; except: import tomli
from typing import List, Dict, Tuple Устарело на 3.10+ list, dict, tuple встроенные
open("f.txt") без encoding Platform-dependent open("f.txt", encoding="utf-8")
os.system() Shell injection + нет контроля subprocess.run()
eval() / exec() с данными Произвольное выполнение кода Парсеры (json, ast.literal_eval)
import * Загрязнение namespace Явные импорты
sys.path.insert(0, ...) Хрупкие пути pyproject.toml или PYTHONPATH
datetime.now() без tz Зависит от системных настроек datetime.now(tz=timezone.utc)

1.4. Декларация совместимости

В начале каждого скрипта (не библиотечного модуля):

#!/usr/bin/env python3
"""
Описание модуля.

Requires: Python >= 3.10
Dependencies: httpx, pydantic
"""

В pyproject.toml проекта:

[project]
requires-python = ">=3.10"
dependencies = [
    "pydantic>=2.0",
    "httpx>=0.25",
]

2. JAVASCRIPT / TYPESCRIPT

2.1. Таблица рисков

Риск Причина Проявление Решение
CJS vs ESM Два несовместимых модульных стандарта require is not defined / import not found Только ESM ("type": "module")
import.meta.url Не работает в CJS SyntaxError ESM only — безопасно
Top-level await Только ESM SyntaxError в CJS ESM only — безопасно
structuredClone Node 17+ / Chrome 98+ ReferenceError Безопасно: минимум Node 20
Array.at() Node 16.6+ TypeError Безопасно: минимум Node 20
crypto.randomUUID() Node 14.17+ TypeError Безопасно: минимум Node 20
fetch глобальный Node 18+ (experimental), Node 21+ (stable) ReferenceError Безопасно: минимум Node 20, но помечен experimental
Legacy decorators vs TC39 Разная семантика Ошибки runtime Только TC39 ("experimentalDecorators": false)
moduleResolution node vs node16 vs bundler Разные пути резолва Использовать "node16" или "bundler"
.js в ESM импортах TypeScript требует .js extension при node16 ERR_MODULE_NOT_FOUND Всегда указывать расширение
Path separators Windows \ vs Unix / Сломанные пути path.join() / path.resolve()

2.2. ОБЯЗАТЕЛЬНЫЕ правила (MUST)

M-JS-01. Только ESM:

// package.json
{
  "type": "module",
  "engines": {
    "node": ">=20"
  }
}

M-JS-02. TypeScript — strict конфигурация:

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "target": "ES2022",
    "module": "node16",
    "moduleResolution": "node16",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "outDir": "dist",
    "declaration": true,
    "isolatedModules": true,
    "experimentalDecorators": false
  }
}

M-JS-03. Импорты — с расширением:

// ПРАВИЛЬНО
import { handler } from "./handler.js";
import { Config } from "../types/config.js";

// ЗАПРЕЩЕНО (не резолвится в node16)
import { handler } from "./handler";

M-JS-04. Пути — только модуль path:

import { join, resolve } from "node:path";

const configPath = join(import.meta.dirname, "config.json");
// НЕ: const configPath = __dirname + "/config.json"  // __dirname не существует в ESM

M-JS-05. Node.js API — с префиксом node:

// ПРАВИЛЬНО
import { readFile } from "node:fs/promises";
import { createServer } from "node:http";

// ДОПУСТИМО, но не рекомендуется
import { readFile } from "fs/promises";

M-JS-06. Обработка ошибок — типизированная:

// ПРАВИЛЬНО
try {
  await operation();
} catch (error) {
  if (error instanceof CustomError) {
    // обработка
  }
  throw error;
}

// ЗАПРЕЩЕНО
try {
  await operation();
} catch (e: any) {
  console.log(e.message); // unsafe
}

2.3. ЗАПРЕЩЁННЫЕ паттерны (NEVER)

Паттерн Почему запрещён Альтернатива
require() / module.exports CJS — устаревшая система import / export
__dirname / __filename Не существуют в ESM import.meta.dirname, import.meta.filename (Node 21+) или fileURLToPath(import.meta.url)
var Hoisting, scope утечки const / let
any тип (кроме boundary) Отключает проверки unknown + type narrowing
@ts-ignore Маскирует ошибки @ts-expect-error с пояснением
eval() Произвольное выполнение JSON.parse(), Function constructor если unavoidable
== (нестрогое) Неявные преобразования ===
Legacy decorators Другая семантика TC39 decorators или без них
process.env.X! (non-null assert) Падение в runtime Валидация при старте: z.object({ X: z.string() })
fs.readFileSync в async коде Блокирует event loop fs.promises.readFile
new Date().getTime() Менее читаемо Date.now()
setTimeout(fn, 0) для microtask Не microtask queueMicrotask(fn) или Promise.resolve().then(fn)

2.4. Декларация совместимости

// package.json
{
  "name": "@platform/service-name",
  "version": "1.0.0",
  "type": "module",
  "engines": {
    "node": ">=20"
  },
  "files": ["dist"],
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    }
  }
}

3. GO

3.1. Таблица рисков

Риск Причина Проявление Решение
int размер 32 бит на 32-bit платформах Overflow Использовать int64 для значений > 2^31
filepath vs path path — UNIX only, filepath — кроссплатформ Сломанные пути на Windows Всегда path/filepath для FS-операций
time.Now() точность Разная на ОС Flaky тесты Не сравнивать timestamp точно
CGO при кросс-компиляции Нужен C toolchain cgo: exec gcc: not found CGO_ENABLED=0 для кросс-билдов
init() порядок Зависит от имён файлов Непредсказуемая инициализация Избегать init() с side effects
Loop variable capture (< 1.22) Замыкание захватывает одну переменную Все goroutine видят последнее значение Go 1.22+ — исправлено; для 1.21 — копия
slices.Sort vs sort.Slice slices — Go 1.21+ undefined: slices Безопасно: минимум 1.21
generics Go 1.18+ syntax error Безопасно: минимум 1.21
range over integer Go 1.22+ cannot range over 10 ЗАПРЕЩЕНО при минимуме 1.21, ОК при 1.22+
Endianness Big-endian vs little-endian Неправильное чтение бинарных данных binary.LittleEndian / BigEndian явно

3.2. ОБЯЗАТЕЛЬНЫЕ правила (MUST)

M-GO-01. go.mod — явная минимальная версия:

module github.com/platform/service

go 1.21

M-GO-02. Пути — только filepath для FS:

// ПРАВИЛЬНО
configPath := filepath.Join(baseDir, "config", "app.json")

// ЗАПРЕЩЕНО (UNIX only)
configPath := path.Join(baseDir, "config", "app.json")

// ЗАПРЕЩЕНО (hardcoded separator)
configPath := baseDir + "/" + "config" + "/" + "app.json"

M-GO-03. Ошибки — wrapping с контекстом:

// ПРАВИЛЬНО
if err != nil {
    return fmt.Errorf("load config %s: %w", path, err)
}

// ЗАПРЕЩЕНО
if err != nil {
    return err  // потеря контекста
}

// ЗАПРЕЩЕНО
if err != nil {
    return fmt.Errorf("error: %v", err)  // %v теряет unwrap цепочку
}

M-GO-04. Goroutine — всегда с recovery:

go func() {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("panic in goroutine: %v\n%s", r, debug.Stack())
        }
    }()
    doWork()
}()

M-GO-05. Context — всегда первый параметр:

func FetchData(ctx context.Context, url string) ([]byte, error) {
    req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
    // ...
}

M-GO-06. Числовые типы — явные размеры для данных:

// ПРАВИЛЬНО — для бизнес-данных, протоколов, файлов
type Order struct {
    ID        int64   // явный размер
    Amount    int64   // копейки
    Quantity  int32   // не превысит 2B
}

// ДОПУСТИМО — для индексов, счётчиков, длин
for i := 0; i < len(items); i++ {  // int ОК

M-GO-07. Кросс-компиляция:

# Сборка для Linux (с сервера — стандартный случай)
go build -o bin/service ./cmd/service

# Кросс-компиляция (для ПК оператора)
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/service-linux ./cmd/service
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o bin/service.exe ./cmd/service

3.3. ЗАПРЕЩЁННЫЕ паттерны (NEVER)

Паттерн Почему запрещён Альтернатива
path.Join для файлов UNIX-only filepath.Join
"/" hardcoded separator UNIX-only filepath.Separator или filepath.Join
init() с side effects Непредсказуемый порядок Явная инициализация в main()
panic() в библиотечном коде Убивает вызывающий код return error
_ = err (игнорирование ошибки) Скрытые сбои Обработать или явно задокументировать
go func(){}() без recover Panic убивает процесс Обёртка с defer recover()
range over int Go 1.22+ only (если минимум 1.21) for i := 0; i < n; i++
range over func Go 1.23+ only Обычные циклы
unsafe.Pointer Ломает memory safety Безопасные альтернативы
reflect без необходимости Медленно, хрупко Generics (1.18+) или кодогенерация
time.Sleep для синхронизации Race condition sync.WaitGroup, channels, context
map из горутин без мьютекса Data race sync.Mutex или sync.Map

3.4. Декларация совместимости

// go.mod
module github.com/platform/service

go 1.21

require (
    // зависимости с точными версиями
)

В //go:build для платформенного кода:

//go:build linux
// +build linux

package main

4. BASH

4.1. Таблица рисков

Риск Причина Проявление Решение
[[ двойные скобки bash-only, нет в sh/dash [[: not found Использовать [[ + shebang #!/bin/bash
declare -A bash 4+ only declare: -A: invalid option Безопасно: минимум bash 5.0
mapfile / readarray bash 4+ mapfile: command not found Безопасно: минимум bash 5.0
<<< here-string bash-only parse error echo "$var" \| cmd как POSIX-альтернатива
set -o pipefail bash/zsh only, не POSIX illegal option Безопасно: shebang #!/bin/bash
$RANDOM bash-only пустая строка в sh Безопасно: shebang #!/bin/bash
local bash/dash/zsh, не строгий POSIX sh local: not found Безопасно: shebang #!/bin/bash
#!/bin/bash vs #!/usr/bin/env bash Разный путь на разных ОС bad interpreter #!/usr/bin/env bash
Неэкранированные переменные Globbing + word splitting rm -rf / при пустой переменной Всегда "${var}" в кавычках
cd без проверки Выполнение в неправильной директории Удаление не тех файлов cd /path || exit 1
Command injection Нефильтрованный ввод в команды Произвольное выполнение Валидация, кавычки

4.2. ОБЯЗАТЕЛЬНЫЕ правила (MUST)

M-SH-01. Заголовок каждого скрипта:

#!/usr/bin/env bash
set -euo pipefail

M-SH-02. Переменные — всегда в кавычках:

# ПРАВИЛЬНО
echo "${name}"
rm -rf "${target_dir:?}"  # :? — ошибка если пустая (защита от rm -rf /)
cp "${source}" "${dest}"

# ЗАПРЕЩЕНО
echo $name       # word splitting + globbing
rm -rf $dir/     # если $dir пустой → rm -rf /

M-SH-03. cd — только с проверкой:

# ПРАВИЛЬНО
cd "${workdir}" || { echo "FATAL: cannot cd to ${workdir}"; exit 1; }

# ЗАПРЕЩЕНО
cd $dir
rm -rf *

M-SH-04. Временные файлы — mktemp:

# ПРАВИЛЬНО
tmpfile=$(mktemp)
trap 'rm -f "${tmpfile}"' EXIT

# ЗАПРЕЩЕНО
tmpfile="/tmp/my_script_temp"  # race condition, collision

M-SH-05. Функции — с local переменными:

do_backup() {
    local source="${1:?source required}"
    local dest="${2:?dest required}"
    local timestamp
    timestamp=$(date '+%Y%m%d_%H%M%S')

    cp -r "${source}" "${dest}/${timestamp}"
}

M-SH-06. Условия — двойные скобки:

# ПРАВИЛЬНО (bash)
if [[ -f "${config}" ]]; then
    source "${config}"
fi

if [[ "${status}" == "active" ]]; then
    echo "running"
fi

# ПРАВИЛЬНО — числовое сравнение
if (( count > 10 )); then
    echo "too many"
fi

M-SH-07. Логирование — функция:

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"; }
err() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1" >&2; }

log "Starting backup"
err "Failed to connect" && exit 1

M-SH-08. Проверка зависимостей:

# В начале скрипта
for cmd in curl jq rsync; do
    command -v "${cmd}" >/dev/null 2>&1 || {
        err "Required command not found: ${cmd}"
        exit 1
    }
done

4.3. ЗАПРЕЩЁННЫЕ паттерны (NEVER)

Паттерн Почему запрещён Альтернатива
#!/bin/sh для bash-скриптов dash не поддерживает bash-фичи #!/usr/bin/env bash
Переменные без кавычек $var Word splitting, globbing "${var}"
[ -f $file ] одинарные скобки Нет pattern matching, broken при пробелах [[ -f "${file}" ]]
cd dir без \|\| exit Молча продолжает в неверной директории cd dir \|\| exit 1
rm -rf ${dir}/ Если $dir пустой → rm -rf / rm -rf "${dir:?}/"
eval "$user_input" Command injection Прямое выполнение без eval
cat file \| grep Useless Use of Cat grep pattern file
echo ${password} Утечка в логи / ps Переменные окружения, файлы
Backticks `cmd` Нечитаемо, не вложимо $(cmd)
function name { Нестандартный синтаксис name() {
for f in $(ls *.txt) Ломается на пробелах for f in *.txt; do
IFS без восстановления Ломает последующий парсинг local IFS=... внутри функции
Hardcoded пути /home/user/ Непортабельно "${HOME}", переменные конфигурации

4.4. Декларация совместимости

В шапке каждого скрипта:

#!/usr/bin/env bash
# ═══════════════════════════════════════════
# НАЗВАНИЕ: описание
# REQUIRES: bash >= 5.0, curl, jq
# PLATFORM: Linux (Ubuntu 22.04+)
# ═══════════════════════════════════════════
set -euo pipefail

5. СВОДНЫЕ ПРАВИЛА ДЛЯ ВСЕХ ЯЗЫКОВ

5.1. Encoding

Правило Применимость
Все исходные файлы — UTF-8 без BOM Все языки
Все файловые операции — явный encoding="utf-8" Python
Все строки — UTF-8 Go (нативно)
.editorconfig в корне проекта Все
# .editorconfig
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4

[*.{js,ts,json,yml,yaml}]
indent_size = 2

[*.go]
indent_style = tab

[Makefile]
indent_style = tab

5.2. Line endings

Правило Где
LF (\n) only Все файлы, все платформы
.gitattributes запрещает CRLF Корень репозитория
# .gitattributes
* text=auto eol=lf
*.sh text eol=lf
*.py text eol=lf
*.go text eol=lf
*.js text eol=lf
*.ts text eol=lf

5.3. Paths

Язык Правильно Неправильно
Python Path(__file__).parent / "config.json" __file__ + "/../config.json"
JS/TS path.join(base, "config.json") base + "/config.json"
Go filepath.Join(base, "config.json") base + "/" + "config.json"
Bash "${SCRIPT_DIR}/config.json" ./config.json (зависит от cwd)

5.4. Определение директории скрипта

# Python
from pathlib import Path
SCRIPT_DIR = Path(__file__).resolve().parent
// TypeScript (ESM, Node.js 20+)
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Go — compile-time, не нужно
// Конфиги через флаги или embed
//go:embed config.json
var configData []byte
# Bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

6. CI/CD ПРОВЕРКИ

6.1. Python

# Линтинг + совместимость
ruff check --target-version py310 .

# Проверка типов
mypy --python-version 3.10 --strict .

# Проверка зависимостей
pip-audit

# Проверка encoding
grep -rn "open(" --include="*.py" | grep -v "encoding="
# ^ все вхождения без encoding — WARNING

6.2. JavaScript / TypeScript

# TypeScript — строгая компиляция
tsc --noEmit

# Линтинг
eslint --max-warnings 0 .

# Проверка ESM
# package.json должен содержать "type": "module"
node -e "import('./src/index.js')"

# Проверка зависимостей
npm audit

6.3. Go

# Vet — стандартные проверки
go vet ./...

# Staticcheck — расширенные проверки
staticcheck ./...

# Тесты с race detector
go test -race ./...

# Проверка кросс-компиляции
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ./...
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build ./...

# Уязвимости
govulncheck ./...

6.4. Bash

# ShellCheck — статический анализ
shellcheck -s bash scripts/*.sh

# Обязательные проверки ShellCheck:
# SC2086 — переменные без кавычек
# SC2046 — word splitting в подстановке
# SC2006 — backticks вместо $()
# SC2064 — trap с раскрытием переменных
# SC2155 — local + подстановка в одной строке

# Проверка shebang
head -1 scripts/*.sh | grep -v "#!/usr/bin/env bash"
# ^ все несоответствия — ERROR

6.5. Универсальные проверки

# Encoding — все файлы UTF-8
file --mime-encoding $(find . -name "*.py" -o -name "*.sh" -o -name "*.go" -o -name "*.ts") \
    | grep -v "utf-8\|us-ascii"
# ^ любые результаты — ERROR

# Line endings — только LF
grep -rlP "\r\n" --include="*.py" --include="*.sh" --include="*.go" --include="*.ts" .
# ^ любые результаты — ERROR

# Secrets в коде
grep -rn "password\s*=\s*['\"]" --include="*.py" --include="*.ts" --include="*.go" .
grep -rn "api_key\s*=\s*['\"]" --include="*.py" --include="*.ts" --include="*.go" .
# ^ любые результаты — CRITICAL

7. ТАБЛИЦА РЕШЕНИЙ ПО ВЕРСИЯМ

Как действовать когда нужна фича из более новой версии.

Python

Фича Версия Действие
match/case 3.10+ Использовать (в рамках минимума)
list[int] 3.9+ (3.10 для union) Использовать
X \| Y union 3.10+ Использовать
tomllib 3.11+ Backport: tomli
ExceptionGroup 3.11+ Не использовать. asyncio.gather(return_exceptions=True)
type X = ... 3.12+ TypeAlias из typing
f-string вложенные 3.12+ format()
Free-threaded (no GIL) 3.13+ Не рассчитывать

JavaScript / TypeScript

Фича Версия Действие
fetch global Node 18+ Использовать (Node 20+, experimental)
import.meta.dirname Node 21+ fileURLToPath + dirname как fallback
structuredClone Node 17+ Использовать
Array.findLast Node 18+ Использовать
import attributes Node 21+ Не использовать. createRequire для JSON
using keyword TS 5.2+ Использовать с осторожностью

Go

Фича Версия Действие
Generics 1.18+ Использовать
slices, maps 1.21+ Использовать
min(), max() 1.21+ Использовать
range over int 1.22+ Только если go 1.22 в go.mod
Loop variable fix 1.22+ Только если go 1.22 в go.mod; иначе — копия
range over func 1.23+ Не использовать
log/slog 1.21+ Использовать

Bash

Фича Версия Действие
declare -A bash 4.0+ Использовать (минимум 5.0)
mapfile bash 4.0+ Использовать
${var@U} (uppercase) bash 5.1+ Использовать с осторожностью
BASH_ARGV0 bash 5.0+ Использовать
Nameref declare -n bash 4.3+ Использовать

8. КОНТРОЛЬНЫЙ СПИСОК ДЛЯ РЕВЬЮ

При code review проверять:

Все языки

Python

JavaScript / TypeScript

Go

Bash


9. МИГРАЦИЯ СУЩЕСТВУЮЩЕГО КОДА

Текущее состояние платформы

На сервере dev-pro-eu (2026-04-05):
- Python 3.10.12
- Node.js 22.21.0
- Go 1.18.1 (ниже минимума 1.21 — требуется обновление)
- Bash 5.1.16

Приоритеты

Приоритет Действие Где
P0 Обновить Go до 1.21+ сервер dev-pro-eu
P1 Добавить set -euo pipefail во все bash-скрипты infra/, .claude/scripts/
P2 Заменить from typing import List, Dict на встроенные library/
P3 Добавить encoding="utf-8" в open() library/, projects/
P4 Добавить .editorconfig и .gitattributes в корень workspace root

Паттерн миграции

Не переписывать всё сразу. При каждом изменении файла — привести к стандарту:

Открыл файл → Исправил задачу → Привёл к стандарту → Коммит

ССЫЛКИ


Версия: 1.0.0