Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b24e8847f | ||
|
|
cfc79894b8 | ||
|
|
9d837f5f5b |
@@ -1,54 +0,0 @@
|
|||||||
name: Check external links
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 6 * * 1" # понедельник 06:00 UTC
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
issues: write
|
|
||||||
actions: write # для keepalive-шага
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
link-check:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Run lychee
|
|
||||||
id: lychee
|
|
||||||
uses: lycheeverse/lychee-action@v2
|
|
||||||
with:
|
|
||||||
# остальные настройки — в lychee.toml в корне репозитория
|
|
||||||
args: --no-progress './**/*.md'
|
|
||||||
fail: false
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
# если открытый issue с меткой link-check уже есть — обновляем его,
|
|
||||||
# а не создаём дубликат каждую неделю
|
|
||||||
- name: Find open link-check issue
|
|
||||||
if: steps.lychee.outputs.exit_code != 0
|
|
||||||
id: issue
|
|
||||||
run: |
|
|
||||||
echo "number=$(gh issue list --repo "$GITHUB_REPOSITORY" --label link-check --state open --json number --jq '.[0].number // empty')" >> "$GITHUB_OUTPUT"
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
|
|
||||||
- name: Create or update issue on broken links
|
|
||||||
if: steps.lychee.outputs.exit_code != 0
|
|
||||||
uses: peter-evans/create-issue-from-file@v5
|
|
||||||
with:
|
|
||||||
title: "Битые внешние ссылки: еженедельная проверка"
|
|
||||||
content-filepath: ./lychee/out.md
|
|
||||||
labels: link-check
|
|
||||||
issue-number: ${{ steps.issue.outputs.number }}
|
|
||||||
|
|
||||||
# GitHub отключает scheduled-workflows после 60 дней без активности
|
|
||||||
# в репозитории; повторное включение сбрасывает таймер
|
|
||||||
- name: Keep scheduled workflow enabled
|
|
||||||
if: always()
|
|
||||||
run: gh api -X PUT "repos/$GITHUB_REPOSITORY/actions/workflows/check-links.yml/enable"
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
name: Deploy MkDocs to GitHub Pages
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pages: write
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: "pages"
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: "3.x"
|
|
||||||
|
|
||||||
- run: pip install mkdocs-material==9.6.14 mkdocs-same-dir==0.1.3
|
|
||||||
|
|
||||||
- run: mkdocs build --strict
|
|
||||||
|
|
||||||
- uses: actions/upload-pages-artifact@v3
|
|
||||||
with:
|
|
||||||
path: site/
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
needs: build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
environment:
|
|
||||||
name: github-pages
|
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
|
||||||
steps:
|
|
||||||
- id: deployment
|
|
||||||
uses: actions/deploy-pages@v4
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
.env
|
.env
|
||||||
.idea
|
.idea
|
||||||
.internal/
|
.internal/
|
||||||
site/
|
|
||||||
tmp
|
|
||||||
@@ -1,12 +1,9 @@
|
|||||||
# Repository Guidelines
|
# Repository Guidelines
|
||||||
|
|
||||||
## Project Structure & Module Organization
|
## Project Structure & Module Organization
|
||||||
- Root `README.md` describes the learning roadmap (RU) and serves as the main page of the MkDocs site.
|
- Root `README.md` describes the learning roadmap (RU).
|
||||||
- `dwh-modeling/` contains the article and demo DWH model; SQL lives in `dwh-modeling/sql` as ordered scripts `01_...sql`–`09_...sql` (07–09 are homework DDL, template and solution).
|
- `dwh-modeling/` contains the article and demo DWH model; SQL lives in `dwh-modeling/sql` as ordered scripts `01_...sql`–`06_...sql`.
|
||||||
- `postgres-bookings/` is a Dockerized PostgreSQL + demo “bookings” DB; start it first, then apply DWH scripts against the `demo` database.
|
- `postgres-bookings/` is a Dockerized PostgreSQL + demo “bookings” DB; start it first, then apply DWH scripts against the `demo` database.
|
||||||
- `mkdocs.yml` — MkDocs Material config; `docs_dir: .` (repo root = site root). Excluded dirs: `project/`, `postgres-bookings/`, `.github/`, `.claude/`.
|
|
||||||
- `.github/workflows/deploy-site.yml` — CI/CD: push to `main` → build → deploy to GitHub Pages.
|
|
||||||
- `project/` — PRD, ADR, and TODO.md (excluded from site). `project/TODO.md` is the prioritized project backlog: check it when planning or proposing work, and mark items done there when you complete them.
|
|
||||||
|
|
||||||
## Build, Test, and Development Commands
|
## Build, Test, and Development Commands
|
||||||
- Start demo Postgres:
|
- Start demo Postgres:
|
||||||
@@ -23,55 +20,14 @@
|
|||||||
- SQL files: keep numeric prefixes (`01_`, `02_`, …) to reflect execution order and use descriptive suffixes like `ddl_*` / `dml_*`.
|
- SQL files: keep numeric prefixes (`01_`, `02_`, …) to reflect execution order and use descriptive suffixes like `ddl_*` / `dml_*`.
|
||||||
- Shell: target `bash`, prefer simple, POSIX-friendly constructs; mirror the style of existing scripts in `postgres-bookings/`.
|
- Shell: target `bash`, prefer simple, POSIX-friendly constructs; mirror the style of existing scripts in `postgres-bookings/`.
|
||||||
|
|
||||||
## Markdown Style (dual-compatible: GitHub + MkDocs Material)
|
|
||||||
|
|
||||||
All `.md` files MUST render correctly on both GitHub and the MkDocs Material site. Follow these rules:
|
|
||||||
|
|
||||||
**Headings:**
|
|
||||||
- `README.md` (root): start from `##` (H2). Do NOT use `#` (H1) — MkDocs uses H1 as page title, multiple H1s break the right-sidebar TOC.
|
|
||||||
- `dwh-modeling/*.md`: may use `#` (H1) since each file is a separate page on the site.
|
|
||||||
|
|
||||||
**Lists:**
|
|
||||||
- Always leave a **blank line before the first list item** after a paragraph, heading, or any non-list text. Python-Markdown (MkDocs) requires this; GitHub tolerates its absence but blank lines don't hurt.
|
|
||||||
- Use **4-space indentation** for nested lists (not 2-space). GitHub supports both, Python-Markdown requires 4.
|
|
||||||
- Example:
|
|
||||||
```markdown
|
|
||||||
Some introductory text:
|
|
||||||
|
|
||||||
- Item one
|
|
||||||
- Item two
|
|
||||||
- Nested item (4 spaces)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Links:**
|
|
||||||
- Internal links: always use **relative paths to `.md` files**: `[text](dwh-modeling/SCD.md)`. MkDocs resolves them automatically.
|
|
||||||
- Anchor links: use lowercase slugs with single hyphens. Avoid em-dash `—` in headings (it produces `--` in MkDocs slugs vs `-` on GitHub). Use commas or colons instead.
|
|
||||||
|
|
||||||
**Special characters in headings:**
|
|
||||||
- OK: colons `:`, commas `,`, parentheses `()`, guillemets `«»` — stripped equally by both platforms.
|
|
||||||
- Avoid: em-dash `—`, en-dash `–` — slug behavior differs between GitHub and MkDocs.
|
|
||||||
|
|
||||||
## MkDocs Site Commands
|
|
||||||
- Local preview (user starts, ask user to run via `!`):
|
|
||||||
`uv run --with 'mkdocs-material==9.6.14' --with 'mkdocs-same-dir==0.1.3' mkdocs serve`
|
|
||||||
- Build with strict validation (catches broken links/anchors):
|
|
||||||
`uv run --with 'mkdocs-material==9.6.14' --with 'mkdocs-same-dir==0.1.3' mkdocs build --strict`
|
|
||||||
- Visual check via Playwright (when `mkdocs serve` is running on port 8000):
|
|
||||||
`npx playwright screenshot --viewport-size='1280,800' 'http://127.0.0.1:8000/#anchor' /path/to/screenshot.png`
|
|
||||||
Then read the screenshot with the Read tool to inspect rendering. Use `--viewport-size='1280,2000'` for tall pages.
|
|
||||||
- Kill stuck dev server: `lsof -ti :8000 | xargs kill`
|
|
||||||
- Site URL: `https://de.dementev.space/` (старый адрес `https://dementev-dev.github.io/de-roadmap/` отдаёт 404)
|
|
||||||
|
|
||||||
## Testing Guidelines
|
## Testing Guidelines
|
||||||
- There is no dedicated test framework; treat SQL scripts as executable documentation.
|
- There is no dedicated test framework; treat SQL scripts as executable documentation.
|
||||||
- For `dwh-modeling/sql`, run scripts sequentially and rerun `04_validation.sql` after changes to ensure the demo model still loads and basic checks pass.
|
- For `dwh-modeling/sql`, run scripts sequentially and rerun `04_validation.sql` after changes to ensure the demo model still loads and basic checks pass.
|
||||||
- For `postgres-bookings`, after modifications run `docker compose up -d && ./psql_sh` and verify simple queries such as `SELECT COUNT(*) FROM bookings.flights;`.
|
- For `postgres-bookings`, after modifications run `docker compose up -d && ./psql_sh` and verify simple queries such as `SELECT COUNT(*) FROM bookings.flights;`.
|
||||||
|
|
||||||
## Commit & Pull Request Guidelines
|
## Commit & Pull Request Guidelines
|
||||||
|
- Commit messages are short, imperative or descriptive phrases (often in Russian), e.g. `Добавлено оглавление`, `Переработка структуры`; group related edits into a single commit.
|
||||||
**Required:** Read [COMMIT_RULES.md](COMMIT_RULES.md) before making commits.
|
- Pull requests should focus on one topic, include a brief context, list of changes, and manual steps to reproduce or validate (commands you ran, expected results).
|
||||||
|
|
||||||
Pull requests should focus on one topic, include a brief context, list of changes, and manual steps to reproduce or validate (commands you ran, expected results).
|
|
||||||
|
|
||||||
## Security & Configuration Tips
|
## Security & Configuration Tips
|
||||||
- Do not commit personal `.env` files or credentials; use local overrides only.
|
- Do not commit personal `.env` files or credentials; use local overrides only.
|
||||||
|
|||||||
-220
@@ -1,220 +0,0 @@
|
|||||||
# Commit Rules
|
|
||||||
|
|
||||||
Unified commit style for all project contributors. Follows [Conventional Commits](https://www.conventionalcommits.org/) specification.
|
|
||||||
|
|
||||||
## Language
|
|
||||||
|
|
||||||
- **Primary language**: Russian
|
|
||||||
- If language is not specified, use Russian
|
|
||||||
- For AI-generated commits, Russian is mandatory unless task explicitly sets `lang:en`
|
|
||||||
- English is allowed only by explicit instruction (`lang:en`) or external collaboration requirements
|
|
||||||
- Do not mix languages in free-text parts of one commit message (subject + body + footer)
|
|
||||||
- Conventional Commit `type(scope)` stays in English
|
|
||||||
- Technical terms (PostgreSQL, SQL, DWH, DDL, DML) keep as-is
|
|
||||||
|
|
||||||
## Header Format
|
|
||||||
|
|
||||||
```
|
|
||||||
<type>(<scope>): <short description>
|
|
||||||
```
|
|
||||||
|
|
||||||
- Maximum header length: 72 characters
|
|
||||||
- For Russian subject, use result form (e.g. "добавлено", "исправлено", "обновлено")
|
|
||||||
- For English subject, use imperative present form (e.g. "add", "fix", "update")
|
|
||||||
- For English subject, do not use past forms (e.g. "added", "fixed", "updated")
|
|
||||||
- No trailing period
|
|
||||||
- Keep subject specific; avoid vague messages like "update", "fix bug", "changes"
|
|
||||||
|
|
||||||
### Allowed `type`
|
|
||||||
|
|
||||||
| Type | Description |
|
|
||||||
|------|-------------|
|
|
||||||
| `feat` | New feature |
|
|
||||||
| `fix` | Bug fix |
|
|
||||||
| `refactor` | Code restructuring without behavior change |
|
|
||||||
| `docs` | Documentation only |
|
|
||||||
| `test` | Tests, checks, validations |
|
|
||||||
| `chore` | Maintenance (configs, scripts, hooks) |
|
|
||||||
| `ci` | CI/CD changes |
|
|
||||||
| `perf` | Performance optimization |
|
|
||||||
| `revert` | Revert previous commit |
|
|
||||||
|
|
||||||
### Recommended `scope` for this repo
|
|
||||||
|
|
||||||
| Scope | Used for |
|
|
||||||
|-------|----------|
|
|
||||||
| `sql` | SQL scripts in `dwh-modeling/sql/` |
|
|
||||||
| `modeling` | DWH modeling docs, articles, schemas in `dwh-modeling/` |
|
|
||||||
| `bookings` | Docker PostgreSQL demo in `postgres-bookings/` |
|
|
||||||
| `docs` | Documentation, README, guides |
|
|
||||||
| `data` | Data files (CSV, fixtures) |
|
|
||||||
|
|
||||||
## Body Structure
|
|
||||||
|
|
||||||
For non-trivial changes, body is required. Use bullet points for readability.
|
|
||||||
|
|
||||||
Body is considered required when at least one condition is true:
|
|
||||||
- behavior or API/contract changed
|
|
||||||
- migration, rollback risk, or compatibility impact exists
|
|
||||||
- more than one meaningful file/module changed
|
|
||||||
- fix is non-obvious from header alone
|
|
||||||
|
|
||||||
### Multiline body in CLI (important)
|
|
||||||
|
|
||||||
- Do not pass body as one quoted string with `\n` (it will be stored literally).
|
|
||||||
- Use multiple `-m` flags, or `-F` with heredoc.
|
|
||||||
|
|
||||||
Correct:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git commit \
|
|
||||||
-m "feat(sql): добавлена валидация данных для DWH" \
|
|
||||||
-m "- Зачем:
|
|
||||||
- нужна проверка целостности перед загрузкой
|
|
||||||
- Что:
|
|
||||||
- добавлен скрипт 04_validation.sql
|
|
||||||
- добавлены проверки на NULL и уникальность
|
|
||||||
- Проверка:
|
|
||||||
- psql -f dwh-modeling/sql/04_validation.sql"
|
|
||||||
```
|
|
||||||
|
|
||||||
Also correct:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git commit -F- <<'MSG'
|
|
||||||
feat(sql): добавлена валидация данных для DWH
|
|
||||||
|
|
||||||
- Зачем:
|
|
||||||
- нужна проверка целостности перед загрузкой
|
|
||||||
- Что:
|
|
||||||
- добавлен скрипт 04_validation.sql
|
|
||||||
- добавлены проверки на NULL и уникальность
|
|
||||||
- Проверка:
|
|
||||||
- psql -f dwh-modeling/sql/04_validation.sql
|
|
||||||
MSG
|
|
||||||
```
|
|
||||||
|
|
||||||
### Template (Russian - default)
|
|
||||||
|
|
||||||
```
|
|
||||||
<type>(<scope>): <краткое описание результата>
|
|
||||||
|
|
||||||
- Зачем:
|
|
||||||
- причина изменения
|
|
||||||
- Что:
|
|
||||||
- ключевое изменение 1
|
|
||||||
- ключевое изменение 2
|
|
||||||
- Проверка:
|
|
||||||
- как проверено
|
|
||||||
```
|
|
||||||
|
|
||||||
### Template (English - only with `lang:en`)
|
|
||||||
|
|
||||||
```
|
|
||||||
<type>(<scope>): <short action description>
|
|
||||||
|
|
||||||
- Why:
|
|
||||||
- reason for change
|
|
||||||
- What:
|
|
||||||
- key change 1
|
|
||||||
- key change 2
|
|
||||||
- Check:
|
|
||||||
- how verified (command/test/smoke-check)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Commit Scope Rules
|
|
||||||
|
|
||||||
- One commit = one logical task
|
|
||||||
- Don't mix feature changes with large refactoring
|
|
||||||
- Update docs in the same commit where behavior changes
|
|
||||||
|
|
||||||
## Breaking Changes
|
|
||||||
|
|
||||||
Use `!` in header for breaking changes:
|
|
||||||
```
|
|
||||||
feat(sql)!: rename stg_orders column contract
|
|
||||||
```
|
|
||||||
|
|
||||||
Add footer:
|
|
||||||
```
|
|
||||||
BREAKING CHANGE: column order_date renamed to created_at
|
|
||||||
```
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Good examples
|
|
||||||
|
|
||||||
```
|
|
||||||
feat(sql): добавлен скрипт загрузки DM-слоя
|
|
||||||
|
|
||||||
- Зачем:
|
|
||||||
- нужны витрины для аналитики
|
|
||||||
- Что:
|
|
||||||
- добавлен 06_dml_dm.sql с загрузкой фактов и измерений
|
|
||||||
- добавлены индексы для оптимизации запросов
|
|
||||||
- Проверка:
|
|
||||||
- psql -f dwh-modeling/sql/06_dml_dm.sql
|
|
||||||
- SELECT COUNT(*) FROM dm.fact_orders;
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
fix(bookings): исправлен порт в docker-compose.yml
|
|
||||||
|
|
||||||
- Зачем:
|
|
||||||
- конфликт с локальным PostgreSQL на 5432
|
|
||||||
- Что:
|
|
||||||
- порт хоста изменен на 5433
|
|
||||||
- Проверка:
|
|
||||||
- docker compose up -d
|
|
||||||
- psql -h localhost -p 5433 -U postgres
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
docs(modeling): обновлена схема Data Vault после ревью
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
chore(docs): синхронизировано оглавление README
|
|
||||||
```
|
|
||||||
|
|
||||||
### Bad examples (don't do this)
|
|
||||||
|
|
||||||
```
|
|
||||||
❌ added sql script # no type, past tense
|
|
||||||
❌ feat: добавлен скрипт # no scope
|
|
||||||
❌ fix: исправлен баг # no scope, vague and non-actionable
|
|
||||||
❌ feat(sql): added new table # past tense in English subject
|
|
||||||
❌ feat(sql): add script and fix validation and update docs # multiple concerns
|
|
||||||
❌ feat(docs): add README и почини SQL # mixed languages in one message
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Reference
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Feature
|
|
||||||
feat(scope): добавлена новая возможность
|
|
||||||
|
|
||||||
# Bug fix
|
|
||||||
fix(scope): исправлена проблема
|
|
||||||
|
|
||||||
# Documentation
|
|
||||||
docs(scope): обновлена документация
|
|
||||||
|
|
||||||
# Refactoring
|
|
||||||
refactor(scope): упрощена структура без изменения поведения
|
|
||||||
|
|
||||||
# Performance
|
|
||||||
perf(scope): ускорено выполнение
|
|
||||||
|
|
||||||
# Maintenance
|
|
||||||
chore(scope): обновлены служебные настройки
|
|
||||||
|
|
||||||
# Feature (lang:en)
|
|
||||||
feat(scope): add new capability
|
|
||||||
|
|
||||||
# Bug fix (lang:en)
|
|
||||||
fix(scope): correct response parsing
|
|
||||||
|
|
||||||
# Documentation (lang:en)
|
|
||||||
docs(scope): update setup guide
|
|
||||||
```
|
|
||||||
@@ -1,58 +1,40 @@
|
|||||||
## О роадмапе
|
# О роадмапе
|
||||||
|
|
||||||
Этот роадмап — конспект моих подходов к обучению Data Engineering.
|
Этот роадмап — конспект моих подходов к обучению Data Engineering.
|
||||||
Он подойдёт тем, кто хочет:
|
Он подойдёт тем, кто хочет:
|
||||||
|
|
||||||
- системно войти в профессию с нуля или близкого к нулю уровня;
|
- системно войти в профессию с нуля или близкого к нулю уровня;
|
||||||
- закрыть пробелы в базе (SQL, Git, Python, DWH, Airflow, Greenplum);
|
- закрыть пробелы в базе (SQL, Git, Python, DWH, Airflow, GreenPlum);
|
||||||
- подготовиться к собеседованиям и первым рабочим задачам.
|
- подготовиться к собеседованиям и первым рабочим задачам.
|
||||||
|
|
||||||
Роадмап можно проходить самостоятельно или вместе со мной в формате менторства.
|
Роадмап можно проходить самостоятельно или вместе со мной в формате менторства.
|
||||||
Если хотите идти с поддержкой ментора — напишите в Telegram: [@dementev_dev](https://t.me/dementev_dev).
|
Если хотите идти с поддержкой ментора — напишите в Telegram: [@dementev_dev](https://t.me/dementev_dev).
|
||||||
|
|
||||||
### Оглавление
|
## Оглавление
|
||||||
|
|
||||||
- [Основные знания](#основные-знания) — Linux, Git, SQL, Python, методологии, Docker
|
- [Основные знания](#основные-знания)
|
||||||
- [Практика и инструменты](#практика-и-инструменты) — Airflow, Greenplum, курсовая работа
|
- [Практика и инструменты](#практика-и-инструменты)
|
||||||
- [Карьера и менторство](#карьера-и-менторство) — резюме, собеседования, испытательный срок
|
- [Карьера и менторство](#карьера-и-менторство)
|
||||||
- [Расширенные навыки](#расширенные-навыки) — Streaming, ClickHouse, Lakehouse, dbt
|
- [Расширенные навыки](#расширенные-навыки)
|
||||||
- [Софт скиллы](#софт-скиллы)
|
- [Софт скиллы](#софт-скиллы)
|
||||||
- [Дополнительные материалы](#дополнительные-материалы)
|
- [Дополнительные материалы](#дополнительные-материалы)
|
||||||
|
|
||||||
Рекомендуемый способ использования:
|
Рекомендуемый способ использования:
|
||||||
|
|
||||||
- двигаться по разделам последовательно, не перепрыгивая через базу;
|
- двигаться по разделам последовательно, не перепрыгивая через базу;
|
||||||
- выполнять практику и домашки, а не только смотреть материалы;
|
- выполнять практику и домашки, а не только смотреть материалы;
|
||||||
- возвращаться к разделам по мере появления реальных задач.
|
- возвращаться к разделам по мере появления реальных задач.
|
||||||
|
|
||||||
---
|
# Основные знания
|
||||||
|
|
||||||
## Основные знания
|
## Git и базовые инструменты
|
||||||
|
|
||||||
[[к оглавлению]](#оглавление)
|
### База по Git
|
||||||
|
|
||||||
### Git и базовые инструменты
|
|
||||||
|
|
||||||
#### Linux и терминал
|
|
||||||
|
|
||||||
Командная строка — рабочее место дата-инженера: docker, psql, git, подключение к серверам живут именно там. Отдельная практика не нужна — все стенды этого роадмапа консольные, команды закрепятся сами. На Windows поставьте [WSL](https://learn.microsoft.com/ru-ru/windows/wsl/install) — полноценный Linux внутри Windows.
|
|
||||||
|
|
||||||
- [Основы Linux для начинающих за 1.5 часа - Youtube](https://www.youtube.com/watch?v=Be6tB59b7D0) — что такое терминал, навигация, файлы, права, ssh: мягкий вход перед статьями
|
|
||||||
- [Linux: Файлы, навигация и поиск - Habr](https://habr.com/ru/articles/1003550/) — перемещение по каталогам, чтение файлов и логов: less, tail, grep
|
|
||||||
- [Права доступа к файлам и папкам в Linux - FirstVDS](https://firstvds.ru/technology/linux-permissions) — rwx, chmod, chown
|
|
||||||
- [SSH для начинающих - Cloud.ru](https://cloud.ru/blog/ssh-dlya-nachinayuschikh) — подключение к удалённой машине
|
|
||||||
- [Основы командной строки - Hexlet](https://ru.hexlet.io/programs/cli-basics) — опционально: бесплатный интерактивный курс с терминалом прямо в браузере; достаточно уроков про навигацию, grep и права доступа
|
|
||||||
|
|
||||||
#### База по Git
|
|
||||||
Что такое контроль версий, когда используется, ПОЧЕМУ и как мы в обучении будем использовать.
|
Что такое контроль версий, когда используется, ПОЧЕМУ и как мы в обучении будем использовать.
|
||||||
Как создать репозиторий на GitHub, сохранять в нем изменения.
|
Как создать репозиторий на GitHub, сохранять в нем изменения.
|
||||||
|
|
||||||
- [Что такое Git для Начинающих / GitHub за 30 минут / Git Уроки - Youtube](https://www.youtube.com/watch?v=VJm_AjiTEEc)
|
- [Что такое Git для Начинающих / GitHub за 30 минут / Git Уроки - Youtube](https://www.youtube.com/watch?v=VJm_AjiTEEc)
|
||||||
- [Git: Конфликты для Начинающих // Git Cherry Pick, Git Revert, Git Reset - Youtube](https://www.youtube.com/watch?v=F7FnnfnB9YY)
|
- [Git: Конфликты для Начинающих // Git Cherry Pick, Git Revert, Git Reset - Youtube](https://www.youtube.com/watch?v=F7FnnfnB9YY)
|
||||||
- Книга [Pro Git](https://git-scm.com/book/ru/v2) - читать главу 1
|
- Книга [Pro Git](https://git-scm.com/book/ru/v2) - читать главу 1
|
||||||
|
|
||||||
#### Основы Markdown
|
### Основы Markdown
|
||||||
|
|
||||||
- [Язык Markdown и файл README | Git и GitHub для начинающих - Youtube](https://www.youtube.com/watch?v=8lEDTrr-G4U)
|
- [Язык Markdown и файл README | Git и GitHub для начинающих - Youtube](https://www.youtube.com/watch?v=8lEDTrr-G4U)
|
||||||
- [Markdown и его возможности: простой способ оформления текста](https://kurshub.ru/journal/blog/markdown-chto-eto/)
|
- [Markdown и его возможности: простой способ оформления текста](https://kurshub.ru/journal/blog/markdown-chto-eto/)
|
||||||
- [Синтаксис Markdown: подробная шпаргалка для веб-разработчиков / Skillbox Media](https://skillbox.ru/media/code/yazyk-razmetki-markdown-shpargalka-po-sintaksisu-s-primerami/)
|
- [Синтаксис Markdown: подробная шпаргалка для веб-разработчиков / Skillbox Media](https://skillbox.ru/media/code/yazyk-razmetki-markdown-shpargalka-po-sintaksisu-s-primerami/)
|
||||||
@@ -60,50 +42,41 @@
|
|||||||
Домашки по остальным темам тренируемся делать в Git, там же пишем документацию.
|
Домашки по остальным темам тренируемся делать в Git, там же пишем документацию.
|
||||||
|
|
||||||
**Когда блок Git и базовые инструменты считаем пройденным:**
|
**Когда блок Git и базовые инструменты считаем пройденным:**
|
||||||
|
|
||||||
- ориентируетесь в терминале: перемещаетесь по каталогам и находите нужное в логах (grep, tail, less);
|
|
||||||
- понимаете права файлов (rwx, chmod) и знаете, как подключиться к серверу по ssh;
|
|
||||||
- вы уверенно создаёте репозиторий, коммитите изменения и отправляете их на GitHub;
|
- вы уверенно создаёте репозиторий, коммитите изменения и отправляете их на GitHub;
|
||||||
- имеете представление о работе с ветками: создание, переключение, что такое merge/PR и разруливание простых конфликтов;
|
- имеете представление о работе с ветками: создание, переключение, что такое merge/PR и разруливание простых конфликтов;
|
||||||
- оформляете базовую документацию в Markdown (README, заголовки, списки, ссылки, кодовые блоки).
|
- оформляете базовую документацию в Markdown (README, заголовки, списки, ссылки, кодовые блоки).
|
||||||
|
|
||||||
### Базы данных: SQL и моделирование данных
|
## SQL
|
||||||
|
### База по SQL
|
||||||
SQL и моделирование данных специально идут рядом: сначала учимся уверенно извлекать данные запросами, затем — понимать и проектировать структуру данных, чтобы ETL/витрины были осмысленными.
|
|
||||||
#### База по SQL
|
|
||||||
Книга: [PostgreSQL. Основы языка SQL](https://postgrespro.ru/education/books/sqlprimer) - Глава 1 "Введение в базы данных и SQL" + ДЗ
|
Книга: [PostgreSQL. Основы языка SQL](https://postgrespro.ru/education/books/sqlprimer) - Глава 1 "Введение в базы данных и SQL" + ДЗ
|
||||||
|
|
||||||
Бесплатный тренажер: [Интерактивный тренажер по SQL – Stepik](https://stepik.org/course/63054/promo)
|
Бесплатный тренажер: [Интерактивный тренажер по SQL – Stepik](https://stepik.org/course/63054/promo)
|
||||||
Целевой уровень знания SQL - Live кодинг на собесе. Проверяем на первом мок-интервью
|
Целевой уровень знания SQL - Live кодинг на собесе. Проверяем на первом мок-интервью
|
||||||
|
**СТЕ**
|
||||||
**CTE**
|
- Зачем нам CTE: [Getting started with CTEs | dbt Labs](https://www.getdbt.com/blog/getting-started-with-cte)
|
||||||
|
- Подробнее про синтаксис: [PostgreSQL : Документация: 17: 7.8. Запросы WITH (Общие табличные выражения) : Компания Postgres Professional](https://postgrespro.ru/docs/postgresql/17/queries-with)
|
||||||
- Зачем нам CTE: [Getting started with CTEs | dbt Labs](https://www.getdbt.com/blog/getting-started-with-cte)
|
|
||||||
- Подробнее про синтаксис: [PostgreSQL : Документация: 17: 7.8. Запросы WITH (Общие табличные выражения) : Компания Postgres Professional](https://postgrespro.ru/docs/postgresql/17/queries-with)
|
|
||||||
|
|
||||||
Для дальнейшей тренировки и поддержания уровня можно использовать [Database - LeetCode](https://leetcode.com/problem-list/database/). Хорошая подборка задачек: [SQL 50 - Study Plan - LeetCode](https://leetcode.com/studyplan/top-sql-50/)
|
Для дальнейшей тренировки и поддержания уровня можно использовать [Database - LeetCode](https://leetcode.com/problem-list/database/). Хорошая подборка задачек: [SQL 50 - Study Plan - LeetCode](https://leetcode.com/studyplan/top-sql-50/)
|
||||||
|
|
||||||
#### Повышение знаний SQL
|
### Повышение знаний SQL
|
||||||
|
|
||||||
Смотрим курс от Postgres Pro [DEV1](https://postgrespro.ru/education/courses/DEV1)
|
Смотрим курс от Postgres Pro [DEV1](https://postgrespro.ru/education/courses/DEV1)
|
||||||
Темы - от "Введение" до "SQL" включительно, "Управление доступом", "Резервное копирование". Для лучшего усваивания материала проделываем все примеры и домашние задания из конспектов лекций.
|
Темы - от "Введение" до "SQL" включительно, "Управление доступом", "Резервное копирование". Для лучшего усваивания материала проделываем все примеры и домашние задания из конспектов лекция.
|
||||||
С темой "PL/pgSQL" можно ознакомиться обзорно.
|
С темой "PL/pgSQL" можно ознакомиться обзорно.
|
||||||
|
|
||||||
Для развития навыков инженера будет полезно лабораторные работы делать не в виртуальной машине, а в docker контейнере. Предложенный (не обязательный) вариант - в каталоге `postgres-bookings` репозитория.
|
Для развития навыков инженера будет полезно лабораторные работы делать не в виртуальной машине, а в docker контейнере. Предложенный (не обязательный) вариант - в каталоге `postgres-bookings` репозитория.
|
||||||
|
|
||||||
Для дальнейшего закрепления материала - читаем книгу [PostgreSQL. Основы языка SQL](https://postgrespro.ru/education/books/sqlprimer)
|
Для дальнейшего закрепления материала - читаем книгу [PostgreSQL. Основы языка SQL](https://postgrespro.ru/education/books/sqlprimer)
|
||||||
|
|
||||||
- Глава 8 - Индексы + ДЗ
|
- Глава 8 - Индексы + ДЗ
|
||||||
- Глава 9 - Транзакции
|
- Глава 9 - Транзакции
|
||||||
- Глава 10 - Повышение производительности + ДЗ
|
- Глава 10 - Повышение производительности + ДЗ
|
||||||
|
|
||||||
Вопросы оптимизации запросов хорошо описаны в курсе [QPT](https://postgrespro.ru/education/courses/QPT) от Postgres Pro. Полученные навыки применимы для работы в том числе с Greenplum, и частично, другими БД.
|
Вопросы оптимизации запросов хорошо описаны в курсе [QPT](https://postgrespro.ru/education/courses/QPT) от Postgres Pro. Полученные навыки применимы для работы в том числе с GreenPlum, и частично, другими БД.
|
||||||
На момент написания, видеолекции были доступны только для старой версии Postgres 13, но ее вполне достаточно.
|
На момент написания, видеолекции были доступны только для старой версии Postgres 13, но ее вполне достаточно.
|
||||||
|
|
||||||
#### Моделирование данных
|
### Моделирование данных
|
||||||
Понимание того, **как устроены данные и зачем они нужны**, — ключ к качественным ETL-процессам.
|
Понимание того, **как устроены данные и зачем они нужны**, — ключ к качественным ETL-процессам.
|
||||||
Мы кратко разбираем:
|
Мы кратко разбираем:
|
||||||
|
|
||||||
- Основные подходы: нормализованные (3NF) vs денормализованные (звезда, снежинка)
|
- Основные подходы: нормализованные (3NF) vs денормализованные (звезда, снежинка)
|
||||||
- Что такое staging, marts, слои raw / clean / business
|
- Что такое staging, marts, слои raw / clean / business
|
||||||
- Как проектировать таблицы под конкретные сценарии использования
|
- Как проектировать таблицы под конкретные сценарии использования
|
||||||
@@ -111,249 +84,176 @@ SQL и моделирование данных специально идут р
|
|||||||
Цель — не стать архитектором, а **уметь читать и объяснять структуру данных**, чтобы писать осмысленные запросы и трансформации.
|
Цель — не стать архитектором, а **уметь читать и объяснять структуру данных**, чтобы писать осмысленные запросы и трансформации.
|
||||||
|
|
||||||
Материалы (включая демо DWH-модель из этого репозитория):
|
Материалы (включая демо DWH-модель из этого репозитория):
|
||||||
|
|
||||||
- Мартин Клеппман — «Высоконагруженные приложения» - Глава 2: Модели данных и языки запросов. - Для понимания, чем реляционная модель (SQL) отличается от документной (NoSQL) и графовой, и почему для аналитики мы всё ещё любим таблицы
|
|
||||||
(Важно: не перепутайте главу 2 с Частью 2 про распределенные данные!).
|
|
||||||
- [Яндекс Практикум: что такое нормализация, простыми словами (для самых начинающих)](https://practicum.yandex.ru/blog/chto-takoe-normalizaciya-dannyh/)
|
- [Яндекс Практикум: что такое нормализация, простыми словами (для самых начинающих)](https://practicum.yandex.ru/blog/chto-takoe-normalizaciya-dannyh/)
|
||||||
- [Базы данных. 1,2,3 нормальные формы. - Youtube](https://www.youtube.com/watch?v=zwQzL80U51c)
|
- [Базы данных. 1,2,3 нормальные формы. - Youtube](https://www.youtube.com/watch?v=zwQzL80U51c)
|
||||||
- [Введение в структуру хранилища данных](dwh-modeling/README.md)
|
- [Введение в структуру хранилища данных](dwh-modeling/README.md)
|
||||||
- Теория про Slowly Changing Dimensions: [SCD](dwh-modeling/SCD.md)
|
- Теория про Slowly Changing Dimensions: [SCD](dwh-modeling/SCD.md)
|
||||||
- (Опционально) Ральф Кимбалл — «Инструментарий хранения и анализа данных» (The Data Warehouse Toolkit) - первые 3 главы
|
|
||||||
- Практика по моделированию статусов клиента: [домашка STG → ODS → DDS → DM](dwh-modeling/Homework_Customer_Status_DDS_DM.md)
|
- Практика по моделированию статусов клиента: [домашка STG → ODS → DDS → DM](dwh-modeling/Homework_Customer_Status_DDS_DM.md)
|
||||||
- Еще про Data Vault:
|
- Еще про Data Vault:
|
||||||
- Конспект и примеры из этого репозитория: [DataVault.md](dwh-modeling/DataVault.md)
|
- Конспект и примеры из этого репозитория: [DataVault.md](dwh-modeling/DataVault.md)
|
||||||
- [DataVault за 10 минут - Youtube](https://www.youtube.com/watch?v=9oQs_wJ045I)
|
- [DataVault за 10 минут - Youtube](https://www.youtube.com/watch?v=9oQs_wJ045I)
|
||||||
- Статья [«Что такое Data Vault: моделирование КХД для архитектора Big Data»](https://bigdataschool.ru/blog/what-is-data-vault/) — обзор, плюсы/минусы, контекст применения.
|
- Статья [«Что такое Data Vault: моделирование КХД для архитектора Big Data»](https://bigdataschool.ru/blog/what-is-data-vault/) — обзор, плюсы/минусы, контекст применения.
|
||||||
- [Гибкие методологии проектирования Data Vault и Anchor Modeling | Евгений Ермаков | karpov.courses](https://www.youtube.com/watch?v=fNGIOb8SJvU)
|
- [Гибкие методологии проектирования Data Vault и Anchor Modeling | Евгений Ермаков | karpov.courses](https://www.youtube.com/watch?v=fNGIOb8SJvU)
|
||||||
- Лекция в рамках курса по DWH: именно [«Основы Data Vault, создаем первую модель»](https://www.youtube.com/watch?v=65b99XCuiR4) — хороший формат: теория + пример.
|
- Лекция в рамках курса по DWH: именно [«Основы Data Vault, создаем первую модель»](https://www.youtube.com/watch?v=65b99XCuiR4) — хороший формат: теория + пример.
|
||||||
- Доклад - практический пример: [Денис Лукьянов — Data Vault 2.0. Когда внедрять, проблемы применения при построении DWH на Greenplum](https://www.youtube.com/watch?v=oGwQbeP5iss)
|
- Доклад - практический пример: [Денис Лукьянов — Data Vault 2.0. Когда внедрять, проблемы применения при построении DWH на Greenplum](https://www.youtube.com/watch?v=oGwQbeP5iss)
|
||||||
|
- Краткая теория про [DWH](https://halltape.github.io/HalltapeRoadmapDE/DWH/) - повторим еще раз, в другом изложении
|
||||||
- Хорошее общее введение в модели данных дано в статье и докладе от Yandex: [Как мы внедрили свою модель хранения данных — highly Normalized hybrid Model. Доклад Яндекса](https://habr.com/ru/companies/yandex/articles/557140/)
|
- Хорошее общее введение в модели данных дано в статье и докладе от Yandex: [Как мы внедрили свою модель хранения данных — highly Normalized hybrid Model. Доклад Яндекса](https://habr.com/ru/companies/yandex/articles/557140/)
|
||||||
|
|
||||||
**Когда блок «Базы данных» считаем пройденным:**
|
**Когда блок SQL считаем пройденным:**
|
||||||
|
|
||||||
- вы уверенно пишете запросы с JOIN, агрегатами, подзапросами и CTE;
|
- вы уверенно пишете запросы с JOIN, агрегатами, подзапросами и CTE;
|
||||||
- можете подробно объяснить план запроса в Postgres, понимаете где планировщик отработал корректно, а где - есть возможность улучшить;
|
- можете подробно объяснить план запроса в Postgres, понимаете где планировщик отработал корректно, а где - есть возможность улучшить;
|
||||||
- можете объяснить простую модель данных (3NF/звезда) и прочитать схему DWH;
|
- можете объяснить простую модель данных (3NF/звезда) и прочитать схему DWH;
|
||||||
- решаете типовые задачи уровня SQL live-coding без долгих пауз.
|
- решаете типовые задачи уровня SQL live-coding без долгих пауз.
|
||||||
|
|
||||||
|
|
||||||
### Python
|
## Python
|
||||||
|
|
||||||
- Если совсем не знакомы с Python, начинаем с курса ["Поколение Python": курс для начинающих – Stepik](https://stepik.org/course/58852/info)
|
- Если совсем не знакомы с Python, начинаем с курса ["Поколение Python": курс для начинающих – Stepik](https://stepik.org/course/58852/info)
|
||||||
- Изучаем глубже и "оттачиваем" live coding: ["Поколение Python": курс для продвинутых – Stepik](https://stepik.org/course/68343/info)
|
- Изучаем глубже и "оттачиваем" live coding: ["Поколение Python": курс для продвинутых – Stepik](https://stepik.org/course/68343/info)
|
||||||
- Продолжение "базы", спрашиваемой на собеседованиях, по Python: ["Поколение Python": курс для профессионалов](https://stepik.org/course/82541/promo). Курс очень полезный, но платный. Вместо него можно почитать "продвинутые" темы дальше.
|
- Продолжение "базы", спрашиваемой на собеседованиях, по Python: ["Поколение Python": курс для профессионалов](https://stepik.org/course/82541/promo). Курс очень полезный, но платный. Вместо него можно почитать "продвинутые" темы дальше.
|
||||||
- "Продвинутые" темы:
|
- "Продвинутые" темы:
|
||||||
- [Полезные функции](https://pyneng.readthedocs.io/ru/latest/book/10_useful_functions/index.html)
|
- [Полезные функции](https://pyneng.readthedocs.io/ru/latest/book/10_useful_functions/index.html)
|
||||||
- [Работа с файлами в формате CSV, JSON, YAML](https://pyneng.readthedocs.io/ru/latest/book/17_serialization/index.html)
|
- [Работа с файлами в формате CSV, JSON, YAML](https://pyneng.readthedocs.io/ru/latest/book/17_serialization/index.html)
|
||||||
- [Итераторы, итерируемые объекты и генераторы](https://pyneng.readthedocs.io/ru/latest/book/13_iterator_generator/index.html)
|
- [Итераторы, итерируемые объекты и генераторы](https://pyneng.readthedocs.io/ru/latest/book/13_iterator_generator/index.html)
|
||||||
- [Декораторы Python: пошаговое руководство](https://habr.com/ru/companies/otus/articles/727590/)
|
- [Декораторы Python: пошаговое руководство](https://habr.com/ru/companies/otus/articles/727590/)
|
||||||
- Работа с датой/временем: [официальная документация по модулю datetime](https://docs.python.org/3/library/datetime.html)
|
- Работа с датой/временем: https://django.fun/docs/python/3.10/library/datetime/
|
||||||
- [Сложность алгоритмов. Разбор Big O](https://habr.com/ru/articles/782608/) — короткий материал, чтобы понимать O(n) vs O(n²) на собеседованиях и в коде
|
|
||||||
- ООП
|
- ООП
|
||||||
- [Tproger: «ООП простыми словами»](https://tproger.ru/experts/oop-in-simple-words)
|
- [Tproger: «ООП простыми словами»](https://tproger.ru/experts/oop-in-simple-words)
|
||||||
- Введение в [ООП](https://metanit.com/python/tutorial/7.1.php)
|
- Введение в [ООП](https://metanit.com/python/tutorial/7.1.php)
|
||||||
- [Яндекс Учебник: «Объектная модель Python: классы, поля и методы»](https://education.yandex.ru/handbook/python/article/obuektnaya-model-python-klassy-polya-i-metody)
|
- [Яндекс Учебник: «Объектная модель Python: классы, поля и методы»](https://education.yandex.ru/handbook/python/article/obuektnaya-model-python-klassy-polya-i-metody)
|
||||||
- [Real Python: OOP in Python (tutorial)](https://realpython.com/python3-object-oriented-programming/)
|
- [Real Python: OOP in Python (tutorial)](https://realpython.com/python3-object-oriented-programming/)
|
||||||
- Виртуальные окружения (venv) — изолируют зависимости проекта; настройте перед установкой Jupyter и библиотек
|
|
||||||
- [Habr: как настроить виртуальное окружение](https://habr.com/ru/articles/889670/)
|
|
||||||
- [SkillFactory: Виртуальные окружения в Python](https://blog.skillfactory.ru/venv-virtualnoe-okruzhenie-v-python/)
|
|
||||||
- Jupyter Lab
|
- Jupyter Lab
|
||||||
- [Блог Практикума: «Что такое Jupyter Notebook: как установить и открыть»](https://practicum.yandex.ru/blog/chto-takoe-jupyter-notebook/)
|
- [Блог Практикума: «Что такое Jupyter Notebook: как установить и открыть»](https://practicum.yandex.ru/blog/chto-takoe-jupyter-notebook/)
|
||||||
- Готовая реализация Jupyter Lab, включающая в себя Spark, в Docker: [jupyter-spark-docker](https://github.com/dementev-dev/jupyter-spark-docker)
|
- Готовая реализация Jupyter Lab, включающая в себя Spark, в Docker: https://github.com/dementev-dev/jupyter-spark-docker
|
||||||
- Pandas
|
- Pandas
|
||||||
- [GeeksforGeeks: “Why Pandas is Used in Python”](https://www.geeksforgeeks.org/pandas/why-pandas-is-used-in-python/)
|
- [GeeksforGeeks: “Why Pandas is Used in Python”](https://www.geeksforgeeks.org/pandas/why-pandas-is-used-in-python/)
|
||||||
- [Skillbox: «Для чего нужна библиотека Pandas»](https://skillbox.ru/media/code/rabotaem-s-pandas-osnovnye-ponyatiya-i-realnye-dannye/)
|
- [Skillbox: «Для чего нужна библиотека Pandas»](https://skillbox.ru/media/code/rabotaem-s-pandas-osnovnye-ponyatiya-i-realnye-dannye/)
|
||||||
- [Official: “10 minutes to pandas”](https://pandas.pydata.org/docs/user_guide/10min.html)
|
- [Official: “10 minutes to pandas”](https://pandas.pydata.org/docs/user_guide/10min.html)
|
||||||
- [Хабр (RUVDS): «Моя шпаргалка по pandas»](https://habr.com/ru/companies/ruvds/articles/494720/)
|
- [Хабр (RUVDS): «Моя шпаргалка по pandas»](https://habr.com/ru/companies/ruvds/articles/494720/)
|
||||||
- [Tproger: «Наглядная шпаргалка по операциям с DataFrame»](https://tproger.ru/articles/pandas-data-wrangling-cheatsheet)
|
- [Tproger: «Наглядная шпаргалка по операциям с DataFrame»](https://tproger.ru/articles/pandas-data-wrangling-cheatsheet)
|
||||||
|
|
||||||
|
|
||||||
Полезно, но дороговато и не обязательно: хорошее комбо SQL + Python — ["Поколение Python": профи + ООП + SQL – Stepik](https://stepik.org/course/233341/promo?search=7181036958)
|
Полезно, но дороговато и не обязательно: хорошее комбо SQL + Python — ["Поколение Python": профи + ООП + SQL – Stepik](https://stepik.org/course/233341/promo?search=7181036958)
|
||||||
|
|
||||||
Цель — уверенно решать простые задачи на Python в формате live-coding; дальше эти навыки пригодятся для создания DAG Airflow.
|
Цель — уверенно решать простые задачи на Python в формате live-coding; дальше эти навыки пригодятся для создания DAG Airflow.
|
||||||
|
|
||||||
**Когда блок Python считаем пройденным:**
|
**Когда блок Python считаем пройденным:**
|
||||||
|
|
||||||
- вы без подсказок пишете небольшие скрипты с циклами, функциями, обработкой ошибок и работой с коллекциями;
|
- вы без подсказок пишете небольшие скрипты с циклами, функциями, обработкой ошибок и работой с коллекциями;
|
||||||
- умеете читать и модифицировать чужой код, в том числе с использованием pandas и DataFrame;
|
- умеете читать и модифицировать чужой код, в том числе с использованием pandas и DataFrame;
|
||||||
- уверенно проходите простой live-coding по Python для DE: прочитать CSV/JSON, отфильтровать, сгруппировать данные и посчитать агрегаты.
|
- уверенно проходите простой live-coding по Python для DE: прочитать CSV/JSON, отфильтровать, сгруппировать данные и посчитать агрегаты.
|
||||||
- можете отвечать как на простые вопросы собеседований (циклы, списки, словари), так и продвинутые (итераторы, декораторы, управление памятью, базовые понятия ООП)
|
- можете отвечать как на простые вопросы собеседований (циклы, списки, словари), так и продвинутые (итераторы, декораторы, управление памятью, базовые понятия ООП)
|
||||||
|
|
||||||
### Методологии разработки
|
## Технические навыки
|
||||||
|
|
||||||
> **Зачем это разработчику?**
|
|
||||||
> 1. **Работа в команде.** Вам нужно понимать «правила игры». Почему задачи двигаются именно так? Зачем мы встречаемся каждое утро на 15 минут? Почему нельзя просто взять задачу из середины списка?
|
|
||||||
> 2. **Собеседование и «легенда».** Когда вас спросят: «Как строилась работа в вашей прошлой команде?», вы должны ответить грамотно. Использование правильной терминологии (спринты, груминг, ретроспектива, WIP-лимиты) — это маркер профессионализма. Это показывает, что вы не просто писали код в вакууме, а были частью налаженного процесса.
|
|
||||||
|
|
||||||
#### 1. Основы и сравнение подходов
|
|
||||||
Для начала нужно понять глобальную разницу между жестким планированием (Waterfall) и гибкой разработкой (Agile).
|
|
||||||
|
|
||||||
- [Waterfall или Agile, Scrum или Kanban: что выбрать](https://habr.com/ru/companies/kaiten/articles/906006/) — *Базовая статья. Читать внимательно, закрывает вопросы и по Waterfall, и по выбору пути.*
|
|
||||||
- [Agile, Scrum, Kanban - обзор, отличия, мифы](https://www.youtube.com/watch?v=LhA5aWjHTJw) — *Видео для закрепления. Помогает разложить кашу в голове по полочкам.*
|
|
||||||
|
|
||||||
#### 2. Agile: Философия гибкости
|
|
||||||
Agile — это не метод, а философия. Scrum и Kanban — это инструменты этой философии.
|
|
||||||
|
|
||||||
- [Scrum vs Kanban: отличия и разница Agile методов](https://kaiten.ru/blog/kanban-vs-scrum/) — *Сравнение двух главных фреймворков. Важно понять, где заканчивается один и начинается другой.*
|
|
||||||
|
|
||||||
#### 3. Scrum (Скрам)
|
|
||||||
Используется, когда мы создаем продукт и работаем спринтами (циклами).
|
|
||||||
*Часто встречается в продуктовых командах, где DE работает в связке с Backend/Frontend.*
|
|
||||||
|
|
||||||
- [Методология Scrum: принципы, ценности, этапы](https://kaiten.ru/blog/chto-takoie-scrum-i-kak-ispolzovat/)
|
|
||||||
|
|
||||||
#### 4. Kanban (Канбан)
|
|
||||||
Используется для управления потоком задач и поддержки.
|
|
||||||
*Наиболее популярен в Data Engineering и DevOps, так как данные поступают непрерывно, и их сложно «запереть» в двухнедельный спринт.*
|
|
||||||
|
|
||||||
- [Канбан: метод, инструменты и принципы](https://kaiten.ru/blog/cto-takoe-kanban/)
|
|
||||||
|
|
||||||
#### Практика: Как это выглядит в жизни
|
|
||||||
|
|
||||||
Теория — это хорошо, но на работе вы увидите конкретный интерфейс (Jira или Yandex Tracker). Важно понимать, куда нажимать и как двигать задачи.
|
|
||||||
|
|
||||||
##### 1. Jira (Мировой стандарт)
|
|
||||||
Самый популярный инструмент. Скорее всего, вы столкнетесь именно с ним.
|
|
||||||
|
|
||||||
* [Как работать с Jira на реальных проектах](https://www.youtube.com/watch?v=oPgm-fsHVfM) (15 мин) — *Отличное видео, где показывают базу: как создать задачу, как перетащить её по доске (Kanban) и что писать в комментариях. Смотреть с 04:00, где начинается практика.*
|
|
||||||
* [Создание и настройка Scrum-досок в JIRA](https://www.youtube.com/watch?v=u-u8NRyApUs) — *Если хотите увидеть, как выглядит Спринт и Бэклог изнутри.*
|
|
||||||
|
|
||||||
##### 2. Yandex Tracker (Российский стандарт)
|
|
||||||
Активно внедряется в крупных компаниях РФ. Логика та же, но интерфейс другой.
|
|
||||||
|
|
||||||
* [Начало работы в Яндекс.Трекере](https://www.youtube.com/watch?v=pdlYiijjn70) (3 мин) — *Супер-короткий официальный гайд. За 3 минуты показывают всё: очереди, доски, карточки.*
|
|
||||||
* [Настройка процесса разработки в Tracker](https://www.youtube.com/watch?v=EdKlYJR2ph0&t=397s) (c 06:37) — *Более глубокий разбор: как выглядит очередь задач разработчика и жизненный цикл тикета.*
|
|
||||||
|
|
||||||
> **💡 Совет:**
|
|
||||||
> Не бойтесь кнопок. Главное правило любого трекера: **«Взял задачу в работу — переведи статус в In Progress»**. Это сигнал команде, что вы заняты и вас лучше не отвлекать.
|
|
||||||
|
|
||||||
**Когда блок «Методологии разработки» считаем пройденным:**
|
|
||||||
|
|
||||||
- вы в общих чертах можете объяснить разницу Waterfall vs Agile и Scrum vs Kanban;
|
|
||||||
- знаете основные мероприятия Scrum (planning / daily / review / retro) и что от вас ожидается на каждом;
|
|
||||||
- умеете работать с трекером (Jira/Tracker): создать/уточнить задачу, взять в работу, корректно двигать статусы и оставлять понятные комментарии;
|
|
||||||
- умеете своевременно сообщать о блокерах и уточнять требования, если задача «не бьётся» или в ней не хватает входных данных.
|
|
||||||
|
|
||||||
### Технические навыки
|
|
||||||
|
|
||||||
#### Продвинутый Git
|
|
||||||
|
|
||||||
|
### Продвинутый Git
|
||||||
- Сжатый, но емкий видеогайд: [GIT, GitHub, GitLab. Полный АКТУАЛЬНЫЙ гайд ЗА ПОЛТОРА ЧАСА. Без этого выгонят с работы - Youtube](https://www.youtube.com/watch?v=0Y-fneoUIO8)
|
- Сжатый, но емкий видеогайд: [GIT, GitHub, GitLab. Полный АКТУАЛЬНЫЙ гайд ЗА ПОЛТОРА ЧАСА. Без этого выгонят с работы - Youtube](https://www.youtube.com/watch?v=0Y-fneoUIO8)
|
||||||
- Книга: [Pro Git](https://git-scm.com/book/ru/v2) - главы
|
- Книга: [Pro Git](https://git-scm.com/book/ru/v2) - главы
|
||||||
- 2 Основы Git
|
- 2 Основы Git
|
||||||
- 3 Ветвление в Git
|
- 3 Ветвление в Git
|
||||||
- 5 Распределённый Git
|
- 5 Распределённый Git
|
||||||
- 6 GitHub
|
- 6 GitHub
|
||||||
- [Курс работы с Git и GitLab - ЭФКО ЦПР | YouTube плейлист](https://www.youtube.com/playlist?list=PLbf8m52BvqlFlblJqQKPuEU26pwgqe7zK). Настоятельно рекомендую проделать за лектором все те действия что он показывает.
|
- [Курс работы с Git и GitLab - ЭФКО ЦПР | YouTube плейлист](https://www.youtube.com/playlist?list=PLbf8m52BvqlFlblJqQKPuEU26pwgqe7zK). Настоятельно рекомендую проделать за лектором все те действия что он показывает.
|
||||||
|
|
||||||
Целевой уровень знания - понимание процесса GitFlow. Как создать ветку, влить изменения в другие ветки. Понимание, зачем.
|
Целевой уровень знания - понимание процесса GitFlow. Как создать ветку, влить изменения в другие ветки. Понимание, зачем.
|
||||||
На собесах обычно не спрашивают, но нужно в работе.
|
На собесах обычно не спрашивают, но нужно в работе.
|
||||||
|
|
||||||
#### Docker
|
### Docker
|
||||||
|
- Курс https://karpov.courses/docker
|
||||||
- Курс [Docker от karpov.courses](https://karpov.courses/docker)
|
|
||||||
|
|
||||||
Основное предназначение для нас - учебные стенды, где мы разбираем и тренируемся с разными технологиями. На работе - иногда пригождается. На собесах спрашивают редко.
|
Основное предназначение для нас - учебные стенды, где мы разбираем и тренируемся с разными технологиями. На работе - иногда пригождается. На собесах спрашивают редко.
|
||||||
|
|
||||||
#### Запись встреч
|
### Методы разработки
|
||||||
OBS Studio
|
|
||||||
|
|
||||||
|
Кратко знакомимся с основными подходами к организации работы в IT:
|
||||||
|
|
||||||
|
- **Водопад** — последовательная разработка,
|
||||||
|
- **Scrum / Kanban** — гибкие методологии, популярные в data-командах.
|
||||||
|
|
||||||
|
Понимание этих концепций помогает быстрее адаптироваться в новых проектах и правильно интерпретировать требования.
|
||||||
|
|
||||||
|
### Запись встреч
|
||||||
|
OBS Studio
|
||||||
- Руководство по OBS: [OBS Studio - Настройка ОБС для Записи Игр и Стрима | Настройка Микрофона в Обс и т.д - Youtube](https://www.youtube.com/watch?v=bj8VEphZ65U)
|
- Руководство по OBS: [OBS Studio - Настройка ОБС для Записи Игр и Стрима | Настройка Микрофона в Обс и т.д - Youtube](https://www.youtube.com/watch?v=bj8VEphZ65U)
|
||||||
- [Как записывать собеседования](https://docs.google.com/document/d/1qd8uRYlAaZp9c5zpvCVBOvYQCEukGHI9PEPjnjahI1k/)
|
- [Как записывать собеседования](https://docs.google.com/document/d/1qd8uRYlAaZp9c5zpvCVBOvYQCEukGHI9PEPjnjahI1k/)
|
||||||
|
|
||||||
**Когда блок технических навыков считаем пройденным:**
|
**Когда блок технических навыков считаем пройденным:**
|
||||||
|
|
||||||
- вы понимаете базовый GitFlow: как организована работа с ветками в команде и как ваши коммиты попадают в прод;
|
- вы понимаете базовый GitFlow: как организована работа с ветками в команде и как ваши коммиты попадают в прод;
|
||||||
- используете Docker для учебных стендов: запускаете контейнеры, смотрите логи и при необходимости перезапускаете сервисы;
|
- используете Docker для учебных стендов: запускаете контейнеры, смотрите логи и при необходимости перезапускаете сервисы;
|
||||||
|
- ориентируетесь в основных методологиях разработки (Scrum/Kanban/водопад) и понимаете, как в них живут задачи и отчётность;
|
||||||
- при необходимости умеете настроить запись экрана/созвонов, чтобы сохранять материалы обучения.
|
- при необходимости умеете настроить запись экрана/созвонов, чтобы сохранять материалы обучения.
|
||||||
|
|
||||||
---
|
# Практика и инструменты
|
||||||
|
|
||||||
## Практика и инструменты
|
## Airflow
|
||||||
|
|
||||||
[[к оглавлению]](#оглавление)
|
|
||||||
|
|
||||||
### Airflow
|
|
||||||
Apache Airflow — инструмент для оркестрации ETL-процессов.
|
Apache Airflow — инструмент для оркестрации ETL-процессов.
|
||||||
|
|
||||||
Мы используем его для:
|
Мы используем его для:
|
||||||
|
|
||||||
- планирования задач,
|
- планирования задач,
|
||||||
- отслеживания зависимостей между шагами,
|
- отслеживания зависимостей между шагами,
|
||||||
- визуализации статуса выполнения.
|
- визуализации статуса выполнения.
|
||||||
|
|
||||||
Материалы:
|
Материалы:
|
||||||
|
|
||||||
- [Учебник по Airflow](https://github.com/dementev-dev/airflow-manual)
|
- [Учебник по Airflow](https://github.com/dementev-dev/airflow-manual)
|
||||||
|
|
||||||
**Когда блок Airflow считаем пройденным:**
|
**Когда блок Airflow считаем пройденным:**
|
||||||
|
|
||||||
- вы можете объяснить, что такое DAG, задачи, операторы и сенсоры, и как между ними задаются зависимости;
|
- вы можете объяснить, что такое DAG, задачи, операторы и сенсоры, и как между ними задаются зависимости;
|
||||||
- на базе учебного стенда подготавливаете, отлаживаете и запускаете свои DAG'и с расписанием и несколькими шагами (например, загрузка данных и последующие трансформации);
|
- на базе учебного стенда подготавливаете, отлаживаете и запускаете свои DAG'и с расписанием и несколькими шагами (например, загрузка данных и последующие трансформации);
|
||||||
- уверенно смотрите логи, находите место падения и понимаете, как перезапустить задачу.
|
- уверенно смотрите логи, находите место падения и понимаете, как перезапустить задачу.
|
||||||
|
|
||||||
### Greenplum
|
## Greenplum
|
||||||
Разбираем, чем Greenplum отличается от PostgreSQL и зачем нужны MPP-хранилища.
|
Разбираем, чем Greenplum отличается от PostgreSQL и зачем нужны MPP-хранилища.
|
||||||
|
|
||||||
#### Фундаментальная теория
|
Предварительно:
|
||||||
Прежде чем нажимать кнопки, нужно понять "физику" больших данных. Почему обычный Postgres начинает тормозить?
|
|
||||||
|
|
||||||
- Мартин Клеппман, "Высоконагруженные приложения":
|
|
||||||
- Глава 1. Надежность, масштабируемость. (Разбираемся, чем вертикальное масштабирование отличается от горизонтального).
|
|
||||||
- Глава 3 (только конец главы). Читаем разделы:
|
|
||||||
- «Обработка транзакций или аналитика?» (OLTP or OLAP?) — ключевое различие нагрузок.
|
|
||||||
- «Хранение по столбцам» — почему аналитика требует другого способа записи данных на диск.
|
|
||||||
- Зачем: Это объясняет, почему Greenplum устроен именно так. Без этого вы будете пытаться работать с ним как с обычным Postgres.
|
|
||||||
|
|
||||||
#### Знакомство с Greenplum
|
|
||||||
Теперь, понимая теорию, смотрим, как это реализовано в конкретном инструменте.
|
|
||||||
|
|
||||||
- Простое введение: [Greenplum | Что это такое и как оно работает? - Youtube](https://www.youtube.com/watch?v=rLG9Z_HcKPY)
|
- Простое введение: [Greenplum | Что это такое и как оно работает? - Youtube](https://www.youtube.com/watch?v=rLG9Z_HcKPY)
|
||||||
|
- Оно же, но текстом: https://halltape.github.io/HalltapeRoadmapDE/GREENPLUM/
|
||||||
- [Визуализатор распределения Greenplum](https://gpskew.rzvde.pro/)
|
- [Визуализатор распределения Greenplum](https://gpskew.rzvde.pro/)
|
||||||
|
- Бесплатный, но большой учебный курс от Yandex: https://yandex.cloud/ru/training/greenplum
|
||||||
|
- [Учебный курс по Greenplum от datafinder](https://datafinder.ru/products/uchebnyy-kurs-po-greenplum) — взять только отдельные главы.
|
||||||
|
|
||||||
**Курс Yandex по Greenplum** — основной учебный курс, рекомендуется пройти целиком:
|
Практика:
|
||||||
|
- [DE Starter Kit — Airflow + Greenplum + CSV](https://github.com/dementev-dev/airflow-greenplum)
|
||||||
|
|
||||||
- [Бесплатный курс Yandex Cloud по Greenplum](https://yandex.cloud/ru/training/greenplum)
|
Сложные варианты с виртуалками — только если менти сильно захочет, в базовый путь не включаем.
|
||||||
- Практику по курсу удобно делать на стенде [airflow-dwh-gp-lab](https://github.com/dementev-dev/airflow-greenplum) — `make up` поднимает рабочий Greenplum с PXF, не нужен облачный кластер.
|
|
||||||
- Стенд покрывает основные темы курса: типы таблиц (heap / appendonly), политики дистрибуции, сжатие, PXF, анализ планов выполнения (`EXPLAIN`).
|
|
||||||
- Единственное ограничение: cloud-специфичные темы (тема 2 курса — развёртывание в Yandex Cloud) на локальном стенде не покрыты.
|
|
||||||
|
|
||||||
**Когда блок Greenplum считаем пройденным:**
|
**Когда блок Greenplum считаем пройденным:**
|
||||||
|
|
||||||
- вы понимаете, как данные распределяются по сегментам, что такое skew и как его увидеть;
|
- вы понимаете, как данные распределяются по сегментам, что такое skew и как его увидеть;
|
||||||
- на базе стенда `airflow-dwh-gp-lab` можете загружать и выгружать данные в Greenplum, выполнять запросы и разбирать планы выполнения (`EXPLAIN`);
|
- на базе учебного стенда (например, DE Starter Kit) можете загружать и выгружать данные в Greenplum, выполнять запросы и разбирать планы выполнения (`EXPLAIN`);
|
||||||
- можете объяснить, в чём практическая разница между MPP-хранилищем и одиночным Postgres на уровне типичных задач DE и собеседований.
|
- можете объяснить, в чём практическая разница между MPP-хранилищем и одиночным Postgres на уровне типичных задач DE и собеседований.
|
||||||
|
|
||||||
### Курсовая работа
|
## Курсовая работа
|
||||||
Курсовая работа — важный майлстоун роадмапа: ваш первый end-to-end data-проект. После неё у вас есть ключевые технические навыки для старта карьеры в Data Engineering.
|
К финалу роадмапа мы собираем небольшую end-to-end курсовую работу — свой первый «боеподобный» data-проект.
|
||||||
|
|
||||||
Курсовая выполняется на том же стенде [airflow-dwh-gp-lab](https://github.com/dementev-dev/airflow-greenplum), который вы уже использовали для практики по Greenplum.
|
### Стенд в Docker Compose
|
||||||
|
- Apache Airflow — оркестратор;
|
||||||
|
- источник данных — TelecomX (или аналогичный открытый датасет);
|
||||||
|
- Greenplum — основное хранилище;
|
||||||
|
- вспомогательный Postgres (по желанию);
|
||||||
|
- ETL-скрипты и DAG'и;
|
||||||
|
- исходные коды всего — в отдельном Git-репозитории.
|
||||||
|
|
||||||
**Что внутри:**
|
В качестве альтернативы файловому источнику можно использовать генератор данных для демо-базы `bookings` от Postgres Pro: https://github.com/postgrespro/demodb.
|
||||||
|
Его удобнее всего встроить в стенд DE Starter Kit (Airflow + Greenplum) как отдельный сервис Postgres с регулярно генерируемыми данными и уже оттуда забирать их в Greenplum (в том числе через PXF, если хочется усложнить архитектуру).
|
||||||
- Стенд содержит DWH с реализованным эталонным срезом (STG → ODS → DDS → DM) — это ваш образец для подражания.
|
|
||||||
- Задача — довести DWH до полного, реализовав недостающие загрузки по аналогии с эталоном.
|
|
||||||
- Есть готовый план от аналитика (ТЗ с маппингами и бизнес-правилами) — не нужно придумывать, что делать.
|
|
||||||
- Встроенная автоматическая проверка реализации поможет убедиться в корректности до проверки ментором.
|
|
||||||
- Ветка `main` — рабочая (с заготовками для реализации), ветка `solution` — эталон для сверки.
|
|
||||||
|
|
||||||
**Когда блок курсовой работы считаем пройденным:**
|
**Когда блок курсовой работы считаем пройденным:**
|
||||||
|
- у вас есть отдельный репозиторий с docker-compose, DAG'ами Airflow, SQL-скриптами и README по проекту;
|
||||||
|
- стенд поднимается локально, DAG'и успешно прогоняются на тестовых данных от загрузки сырья до витрин;
|
||||||
|
- вы можете на собеседовании за 5–10 минут рассказать архитектуру курсового проекта, его цели и показать ключевые части кода.
|
||||||
|
|
||||||
- все загрузки реализованы, DWH заполняется полностью (STG → ODS → DDS → DM);
|
## Понятие сложности алгоритмов
|
||||||
- автоматическая проверка (валидационный DAG) проходит без ошибок;
|
В Data Engineering редко требуется писать сложные алгоритмы, но важно понимать, как оценивать эффективность кода:
|
||||||
- вы можете на собеседовании за 5–10 минут рассказать архитектуру проекта, его цели и показать ключевые части кода.
|
|
||||||
|
|
||||||
---
|
- в SQL — через объём сканируемых данных, типы JOIN’ов, использование индексов;
|
||||||
|
- в Python — через асимптотику операций с pandas/списками (например, O(n) vs O(n²)).
|
||||||
|
|
||||||
## Карьера и менторство
|
Это помогает избегать «тормозящих» решений на собеседованиях и в реальных пайплайнах.
|
||||||
|
|
||||||
[[к оглавлению]](#оглавление)
|
# Карьера и менторство
|
||||||
|
|
||||||
### Менторство по этому роадмапу
|
## Менторство по этому роадмапу
|
||||||
|
|
||||||
Если вы нашли этот роадмап в интернете и хотите пройти его не в одиночку, а с поддержкой ментора, можно присоединиться ко мне.
|
Если вы нашли этот роадмап в интернете и хотите пройти его не в одиночку, а с поддержкой ментора, можно присоединиться ко мне.
|
||||||
|
|
||||||
**Что даёт менторство:**
|
**Что даёт менторство:**
|
||||||
|
|
||||||
- структурный план прохождения роадмапа под вашу ситуацию;
|
- структурный план прохождения роадмапа под вашу ситуацию;
|
||||||
- разбор вопросов по SQL / DWH / Airflow и другим темам из этого документа;
|
- разбор вопросов по SQL / DWH / Airflow и другим темам из этого документа;
|
||||||
- разбор домашних заданий и код-ревью;
|
- разбор домашних заданий и код-ревью;
|
||||||
@@ -364,153 +264,96 @@ Apache Airflow — инструмент для оркестрации ETL-про
|
|||||||
Просто напишите мне в Telegram: [@dementev_dev](https://t.me/dementev_dev)
|
Просто напишите мне в Telegram: [@dementev_dev](https://t.me/dementev_dev)
|
||||||
со словами «Хочу пройти роадмап с ментором» — дальше всё обсудим.
|
со словами «Хочу пройти роадмап с ментором» — дальше всё обсудим.
|
||||||
|
|
||||||
### Подготовка к собеседованиям
|
## Подготовка к собеседованиям
|
||||||
Цель блока — сформировать «опыт от 2 лет» и уметь корректно его показать в резюме и на собеседовании.
|
Цель блока — сформировать «опыт от 2 лет» и уметь корректно его показать в резюме и на собеседовании.
|
||||||
|
|
||||||
#### Помощь в подготовке резюме
|
### Помощь в подготовке резюме
|
||||||
|
|
||||||
- Видео от ОМ по составлению резюме
|
- Видео от ОМ по составлению резюме
|
||||||
- [Как накрутить опыт в резюме | «Ультимативный гайд» @digital_ninja](https://www.youtube.com/watch?v=EPuogJuYsvY)
|
- [Как накрутить опыт в резюме | «Ультимативный гайд» @digital_ninja](https://www.youtube.com/watch?v=EPuogJuYsvY)
|
||||||
- [Как писать резюме, чтобы его читали - доклад - Boosty](https://boosty.to/m0rtymerr/posts/71b02a6b-8116-466a-b945-b2ed793abd8f)
|
- [Как писать резюме, чтобы его читали - доклад - Boosty](https://boosty.to/m0rtymerr/posts/71b02a6b-8116-466a-b945-b2ed793abd8f)
|
||||||
- [Как грамотно продать себя на собеседовании / Созвон сообщества - Boosty](https://boosty.to/m0rtymerr/posts/7289cd23-60c6-4010-bb1c-a5b28dac399a)
|
- [Как грамотно продать себя на собеседовании / Созвон сообщества - Boosty](https://boosty.to/m0rtymerr/posts/7289cd23-60c6-4010-bb1c-a5b28dac399a)
|
||||||
- Практика: совместная работа над резюме — ментор помогает переработать опыт, сформировать убедительную карьерную историю и подготовиться к вопросам по ней.
|
- Попытки менти написать резюме, моя обратная связь — итеративно.
|
||||||
|
|
||||||
#### Поиск работы и собеседования
|
### Навыки поиска работы с HH и Habr карьера
|
||||||
|
|
||||||
- [Как подтвердить опыт без трудовой / Хабр против работяг](https://www.youtube.com/watch?v=GHqABzA1zi8)
|
- [Как подтвердить опыт без трудовой / Хабр против работяг](https://www.youtube.com/watch?v=GHqABzA1zi8)
|
||||||
- Практика: мок-собеседования с ментором — тренировка ответов, разбор слабых мест, психологическая подготовка к реальным интервью.
|
- [Как успешно пройти испытательный срок в IT | «Ультимативный гайд» c @digital_ninja - Youtube](https://www.youtube.com/watch?v=r1lWP5rYVdk)
|
||||||
|
- Видео по прохождению собесов от ОМ.
|
||||||
|
- Мои комментарии к нему, мой опыт
|
||||||
|
- Первые тренировки мок собесы, обратная связь
|
||||||
|
|
||||||
#### Помощь с прохождением испытательного срока
|
### Помощь с прохождением испытательного срока
|
||||||
|
|
||||||
- [Как успешно пройти испытательный срок в IT | «Ультимативный гайд» c @digital_ninja - Youtube](https://www.youtube.com/watch?v=r1lWP5rYVdk)
|
- [Как успешно пройти испытательный срок в IT | «Ультимативный гайд» c @digital_ninja - Youtube](https://www.youtube.com/watch?v=r1lWP5rYVdk)
|
||||||
- [Испытательный срок - доклад - Boosty](https://boosty.to/m0rtymerr/posts/40e7f17e-022b-495c-8d03-dabbe4383b8e)
|
- [Испытательный срок - доклад - Boosty](https://boosty.to/m0rtymerr/posts/40e7f17e-022b-495c-8d03-dabbe4383b8e)
|
||||||
|
|
||||||
**Когда блок подготовки к собеседованиям считаем пройденным:**
|
**Когда блок подготовки к собеседованиям считаем пройденным:**
|
||||||
|
|
||||||
- у вас есть актуальное резюме под DE с понятными примерами проектов вместо «пустого» опыта;
|
- у вас есть актуальное резюме под DE с понятными примерами проектов вместо «пустого» опыта;
|
||||||
- вы умеете искать и отбирать вакансии на HH и Habr Карьера, адаптируя отклики под конкретную позицию;
|
- вы умеете искать и отбирать вакансии на HH и Habr Карьера, адаптируя отклики под конкретную позицию;
|
||||||
- вы прошли хотя бы пару мок-собеседований, получили обратную связь и по результатам доработали резюме и стратегию поиска.
|
- вы прошли хотя бы пару мок-собеседований, получили обратную связь и по результатам доработали резюме и стратегию поиска.
|
||||||
|
|
||||||
---
|
# Расширенные навыки
|
||||||
|
|
||||||
## Расширенные навыки
|
|
||||||
Эти темы выходят за рамки базового минимума для старта в Data Engineering, но дают более полное представление об экосистеме.
|
Эти темы выходят за рамки базового минимума для старта в Data Engineering, но дают более полное представление об экосистеме.
|
||||||
Их цель — понимать, зачем и когда используется тот или иной инструмент, а не осваивать его на уровне администратора или DevOps-инженера.
|
Их цель — понимать, зачем и когда используется тот или иной инструмент, а не осваивать его на уровне администратора или DevOps-инженера.
|
||||||
|
|
||||||
[[к оглавлению]](#оглавление)
|
|
||||||
|
|
||||||
Мы кратко знакомимся с:
|
Мы кратко знакомимся с:
|
||||||
|
|
||||||
- **Streaming** (NiFi + Kafka) — инструментами для построения потоковых и интеграционных пайплайнов;
|
- **Apache NiFi** и **Kafka** — инструментами для построения потоковых и интеграционных пайплайнов;
|
||||||
- **ClickHouse** — колоночной СУБД для высоконагруженной аналитики;
|
- **ClickHouse** — колоночной СУБД для высоконагруженной аналитики;
|
||||||
- **Lakehouse** (Spark, Iceberg, Trino) — архитектурой, построенной на разделении compute и storage;
|
|
||||||
- **dbt** — подходом к трансформации данных как кода.
|
- **dbt** — подходом к трансформации данных как кода.
|
||||||
|
|
||||||
Практика ограничивается минимальным рабочим примером (запуск в Docker, простой пайплайн или SQL-модель).
|
Практика ограничивается минимальным рабочим примером (например, запуск в Docker, простой пайплайн или SQL-модель).
|
||||||
|
|
||||||
Этого достаточно, чтобы уверенно говорить об инструменте на собеседовании и понимать его место в архитектуре — а всё остальное при необходимости осваивается уже на проекте.
|
Этого достаточно, чтобы уверенно говорить об инструменте на собеседовании и понимать его место в архитектуре — а всё остальное при необходимости осваивается уже на проекте.
|
||||||
|
|
||||||
### Streaming (NiFi + Kafka)
|
## ClickHouse
|
||||||
NiFi — визуальный конструктор потоков данных, Kafka — распределённая очередь сообщений. Вместе они закрывают типичный сценарий: принять данные, буферизовать, доставить в хранилище.
|
Бесплатный курс https://yandex.cloud/ru/training/clickhouse
|
||||||
|
Платный курс [ClickHouse для аналитика – Stepik](https://stepik.org/course/100210/promo?search=6551441002)
|
||||||
|
|
||||||
Материалы:
|
## NiFi
|
||||||
|
Плейлист [Apache NiFi с нуля за 3 часа. Конструктор вместо кода - Youtube](https://youtube.com/playlist?list=PL4MpKy3QjNp_rOEEibc4Ro8UK4g8vLX6_&si=W_hidjHmBOZ_aUfS) — первые 4 видео, дальше — по желанию.
|
||||||
- [Apache NiFi с нуля за 3 часа (Youtube-плейлист)](https://youtube.com/playlist?list=PL4MpKy3QjNp_rOEEibc4Ro8UK4g8vLX6_&si=W_hidjHmBOZ_aUfS) — первые 4 видео, дальше — по желанию
|
|
||||||
- [Лучший Гайд по Kafka для Начинающих За 1 Час (Youtube)](https://www.youtube.com/watch?v=hbseyn-CfXY)
|
|
||||||
|
|
||||||
Практика — на стенде [nifi-kafka-postgres-lab](https://github.com/dementev-dev/nifi-kafka-postgres-lab) (Docker Compose с NiFi, Kafka и Postgres):
|
|
||||||
|
|
||||||
- настраиваем в NiFi простой генератор данных и поток в Postgres;
|
|
||||||
- строим поток NiFi → Kafka → NiFi → Postgres.
|
|
||||||
|
|
||||||
Знакомство с Kafka здесь пригодится и дальше: стенд по ClickHouse в следующей секции принимает данные именно через Kafka.
|
|
||||||
|
|
||||||
### ClickHouse
|
|
||||||
ClickHouse — колоночная СУБД для аналитики на больших объёмах: миллиарды строк, агрегации за секунды. В российских компаниях это фактический стандарт для витрин, отчётности и продуктовой аналитики, поэтому на собеседованиях тема всплывает часто.
|
|
||||||
|
|
||||||
Материалы:
|
|
||||||
|
|
||||||
- Бесплатный курс [ClickHouse от Yandex Cloud](https://yandex.cloud/ru/training/clickhouse) — берём за основу, в нём много упражнений
|
|
||||||
- Платный курс [ClickHouse для аналитика – Stepik](https://stepik.org/course/100210/promo?search=6551441002)
|
|
||||||
|
|
||||||
Практика:
|
Практика:
|
||||||
|
- собираем отдельный стенд в Docker Compose с Postgres и NiFi;
|
||||||
|
- в NiFi настраиваем простой генератор данных.
|
||||||
|
|
||||||
- Упражнения курса Яндекса можно выполнять в их облаке (с оплатой за ресурсы) или бесплатно у себя — на учебном кластере [clickhouse-learning-cluster](https://github.com/dementev-dev/clickhouse-learning-cluster): 4 узла ClickHouse в Docker Compose, репликация, шардинг, балансировка через HAProxy.
|
## Kafka
|
||||||
- Следующий шаг — стенд [clickstream-ch-kafka-superset-demo](https://github.com/dementev-dev/clickstream-ch-kafka-superset-demo), имитирующий полноценное аналитическое хранилище на ClickHouse: Kafka, Airflow, дашборды в Superset, мониторинг (Prometheus/Grafana), слои STG → ODS → DDS → DM. Внутри — собственный продвинутый курс «Кликстрим на ClickHouse» с уроками прямо на стенде.
|
[Лучший Гайд по Kafka для Начинающих За 1 Час - Youtube](https://www.youtube.com/watch?v=hbseyn-CfXY)
|
||||||
|
|
||||||
### Lakehouse (Spark, Iceberg, Trino)
|
Практика:
|
||||||
|
- расширяем предыдущий стенд, добавляя Kafka;
|
||||||
|
- строим поток данных: NiFi → Kafka;
|
||||||
|
- добавляем обратный поток: Kafka → NiFi → Postgres.
|
||||||
|
|
||||||
Lakehouse — архитектурный подход, который соединяет гибкость Data Lake с гарантиями классического DWH.
|
## dbt
|
||||||
|
|
||||||
В классическом DWH данные и вычисления живут внутри одной СУБД, в её закрытом формате. В Lakehouse они разделены. Данные лежат файлами в дешёвом хранилище (обычно объектном, вроде S3). Открытый табличный формат (Iceberg, Delta, Hudi) добавляет поверх файлов привычные по СУБД вещи: схемы, транзакции, историю изменений. А вычислительные движки (Spark, Trino, Flink и другие) подключаются к данным снаружи — хоть несколько разных к одним и тем же таблицам.
|
|
||||||
|
|
||||||
Роадмап фокусируется на классическом DWH-стеке, поэтому цель здесь — знакомство, но с настоящей практикой: стенд ниже собирает один из типовых наборов этого конструктора.
|
|
||||||
|
|
||||||
Материалы:
|
|
||||||
|
|
||||||
- Введение в тему: [«Как не утонуть в данных: выбираем между DWH, Data Lake и Lakehouse» (Habr, Arenadata)](https://habr.com/ru/companies/arenadata/articles/885722/) — что такое Lakehouse, чем он отличается от классического DWH и Data Lake и зачем появился
|
|
||||||
- [DataLearn: «Что такое Apache Spark»](https://youtu.be/Tl9YzC-dQLI) — введение в Spark с нуля, ~40 минут
|
|
||||||
|
|
||||||
Практика — курс [«Lakehouse без магии»](https://github.com/dementev-dev/mini-lakehouse-lab) на стенде mini-lakehouse-lab (Spark + Iceberg + Trino + MinIO, всё локально в Docker, без облаков и регистраций):
|
|
||||||
|
|
||||||
- 8 модулей на ~12–15 часов самостоятельной работы; в каждом — объяснение, демонстрация, задание и checkpoint;
|
|
||||||
- пайплайн `raw → bronze → silver` на реальном датасете NYC Taxi;
|
|
||||||
- одна таблица из двух движков: запись через Spark, чтение через Trino — и почему это работает без копирования данных;
|
|
||||||
- schema evolution, time travel и обслуживание таблиц (compaction, expire_snapshots) — с параллелями к знакомым VACUUM/REORGANIZE из мира Postgres/Greenplum.
|
|
||||||
|
|
||||||
Глубже про Iceberg (опционально, лучше после практики на стенде):
|
|
||||||
|
|
||||||
- [«Как на самом деле работает Apache Iceberg» — Владимир Озеров, HighLoad Channel (Youtube)](https://www.youtube.com/watch?v=_3fsE2a2FO4)
|
|
||||||
- [Введение в устройство Parquet и Iceberg (Habr, VK Tech)](https://habr.com/ru/companies/vktech/articles/959398/) — подробный и местами непростой разбор форматов изнутри
|
|
||||||
- [Введение в Apache Iceberg: основы, архитектура, как работает](https://ivan-shamaev.ru/apache-iceberg-tutorial-architecture-how-to-work/#__Apache_Iceberg-2)
|
|
||||||
- [Spark + Iceberg in 1 Hour: Memory Tuning, Joins, Partition (Youtube, англ.)](https://www.youtube.com/watch?v=3R-SLYK-P_0)
|
|
||||||
|
|
||||||
### dbt
|
|
||||||
dbt (data build tool) — инструмент для трансформации данных в хранилище.
|
dbt (data build tool) — инструмент для трансформации данных в хранилище.
|
||||||
|
|
||||||
Мы рассматриваем его как альтернативу «ручному» написанию сложных CTE и для понимания современного подхода к моделированию данных как кода.
|
Мы рассматриваем его как альтернативу «ручному» написанию сложных CTE и для понимания современного подхода к моделированию данных как кода.
|
||||||
|
|
||||||
Материалы:
|
|
||||||
|
|
||||||
- [dbt quickstart (официальный гайд)](https://docs.getdbt.com/guides/manual-install?step=1)
|
|
||||||
- [Перевод документации dbt на русский](https://docs.getdbt.tech/)
|
|
||||||
|
|
||||||
Практика:
|
|
||||||
|
|
||||||
- Клонировать [jaffle-shop](https://github.com/dbt-labs/jaffle-shop), установить dbt-duckdb, прогнать `dbt build`, `dbt test`, `dbt docs generate && dbt docs serve` — этого достаточно, чтобы увидеть весь цикл.
|
|
||||||
|
|
||||||
**Когда блок расширенных навыков считаем пройденным:**
|
**Когда блок расширенных навыков считаем пройденным:**
|
||||||
|
- вы можете на собеседовании кратко объяснить, когда уместны NiFi/Kafka, ClickHouse и dbt, и чем они дополняют базовый стек (Postgres, Airflow, Greenplum);
|
||||||
- вы можете на собеседовании кратко объяснить, когда уместны Streaming (NiFi/Kafka), ClickHouse, Lakehouse-стек и dbt, и чем они дополняют базовый стек (Postgres, Airflow, Greenplum);
|
- понимаете типичные сценарии: потоковые интеграции и очереди (Kafka/NiFi), аналитические витрины и отчёты на ClickHouse, трансформации данных в dbt;
|
||||||
- понимаете типичные сценарии: потоковые интеграции и очереди (NiFi + Kafka), аналитические витрины и отчёты на ClickHouse, Lakehouse-архитектура (Spark/Iceberg/Trino), трансформации данных как код (dbt);
|
|
||||||
- не боитесь увидеть эти инструменты в описании вакансии и можете поддержать содержательный разговор об их месте в архитектуре.
|
- не боитесь увидеть эти инструменты в описании вакансии и можете поддержать содержательный разговор об их месте в архитектуре.
|
||||||
|
|
||||||
---
|
# Софт скиллы
|
||||||
|
|
||||||
## Софт скиллы
|
|
||||||
|
|
||||||
[[к оглавлению]](#оглавление)
|
|
||||||
|
|
||||||
- [Все ветви дохода в IT / Полный гайд по деньгам](https://youtube.com/live/JHClTWwK1EM)
|
- [Все ветви дохода в IT / Полный гайд по деньгам](https://youtube.com/live/JHClTWwK1EM)
|
||||||
- [Гайд как писать отзывы](https://boosty.to/m0rtymerr/posts/b04040ec-0f46-4524-9c75-188a513140ad?share=post_link)
|
- [Гайд как писать отзывы](https://boosty.to/m0rtymerr/posts/b04040ec-0f46-4524-9c75-188a513140ad?share=post_link)
|
||||||
- [Гайд по Антистрессу](https://youtu.be/bu0YiXOKaoU)
|
- [Гайд по Антистрессу](https://youtu.be/bu0YiXOKaoU)
|
||||||
|
|
||||||
---
|
# Дополнительные материалы
|
||||||
|
|
||||||
## Дополнительные материалы
|
|
||||||
|
|
||||||
[[к оглавлению]](#оглавление)
|
|
||||||
|
|
||||||
- [ananevsyu/SandBox_DB_public: Песочница для изучения различных технологий связанных с инженерией данных](https://gitflic.ru/project/ananevsyu/sandbox_db_public)
|
- [ananevsyu/SandBox_DB_public: Песочница для изучения различных технологий связанных с инженерией данных](https://gitflic.ru/project/ananevsyu/sandbox_db_public)
|
||||||
- Клон проекта [dementev_dev/sandbox_db_public-форк](https://gitflic.ru/project/dementev_dev/sandbox_db_public-fork)
|
- Клон проекта [dementev_dev/sandbox_db_public-форк](https://gitflic.ru/project/dementev_dev/sandbox_db_public-fork)
|
||||||
- [System Design. Разбор книги "Высоконагруженные приложения". Глава 1 - Youtube](https://www.youtube.com/watch?v=owjrIB_5go8) — отличный видео-конспект первой главы Клеппмана на русском.
|
|
||||||
- [Индексы в БД - Youtube](https://www.youtube.com/watch?v=DyqtBiDrz3g)
|
- [Индексы в БД - Youtube](https://www.youtube.com/watch?v=DyqtBiDrz3g)
|
||||||
|
- [Spark + Iceberg in 1 Hour - Memory Tuning, Joins, Partition - Youtube](https://www.youtube.com/watch?v=3R-SLYK-P_0)
|
||||||
|
- [Введение в устройство Parquet и Iceberg - habr](https://habr.com/ru/companies/vktech/articles/959398/)
|
||||||
|
- [Введение в Apache Iceberg. Основы, архитектура, как работает?](https://ivan-shamaev.ru/apache-iceberg-tutorial-architecture-how-to-work/#__Apache_Iceberg-2)
|
||||||
- [Алгоритмы: теория и практика. Методы – Stepik](https://stepik.org/course/217/info)
|
- [Алгоритмы: теория и практика. Методы – Stepik](https://stepik.org/course/217/info)
|
||||||
- [Алгоритмы: теория и практика. Структуры данных – Stepik](https://stepik.org/course/1547/promo)
|
- [Алгоритмы: теория и практика. Структуры данных – Stepik](https://stepik.org/course/1547/promo)
|
||||||
- [Apache Hadoop для самых маленьких: HDFS, RACK-AWARENESS, репликация и Data Locality - Youtube](https://youtu.be/0fsY5bW2l84)
|
- [Apache Hadoop для самых маленьких: HDFS, RACK-AWARENESS, репликация и Data Locality - Youtube](https://youtu.be/0fsY5bW2l84)
|
||||||
- [Книга. Введение в Apache Kafka для системных аналитиков и проектировщиков интеграций](https://systems.education/kafka)
|
- [Книга. Введение в Apache Kafka для системных аналитиков и проектировщиков интеграций](https://systems.education/kafka)
|
||||||
- [Перевод документации dbt на русский язык](https://docs.getdbt.tech/)
|
- [Перевод документации dbt на русский язык](https://docs.getdbt.tech/)
|
||||||
|
|
||||||
### Записи ОМ
|
## Записи ОМ
|
||||||
|
|
||||||
- [Как пройти собеседование на программиста | Ультимативный гайд с @om_nazarov - Youtube](https://www.youtube.com/watch?v=tzSdiYZ52kI)
|
- [Как пройти собеседование на программиста | Ультимативный гайд с @om_nazarov - Youtube](https://www.youtube.com/watch?v=tzSdiYZ52kI)
|
||||||
- [Как стать программистом в 2025 | «Ультимативный гайд» с @om_nazarov](https://www.youtube.com/watch?v=6151ekTOl38)
|
- [Как стать программистом в 2025 | «Ультимативный гайд» с @om_nazarov](https://www.youtube.com/watch?v=6151ekTOl38)
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
# Разработка с ИИ
|
|
||||||
|
|
||||||
Практические материалы о работе с ИИ-ассистентами для разработчиков: как эффективно взаимодействовать с coding agents, организовать контекст и память, выстроить рабочий процесс.
|
|
||||||
|
|
||||||
Раздел не привязан к основному роадмапу по Data Engineering и может использоваться независимо.
|
|
||||||
|
|
||||||
## Материалы
|
|
||||||
|
|
||||||
- [Лучшие практики работы с coding agents](best-practice.md): 10 принципов эффективной работы с ИИ-ассистентами, от структурирования задач до автоматизации
|
|
||||||
- [Механизм памяти coding agents](memory-mechanism.md): как устроена память агентов, типы памяти, многоуровневая организация и практические рекомендации
|
|
||||||
|
|
||||||
## Источники
|
|
||||||
|
|
||||||
Материалы раздела подготовлены на основе документации [Z.AI DevPack](https://docs.z.ai/devpack/resources/best-practice).
|
|
||||||
@@ -1,234 +0,0 @@
|
|||||||
# Лучшие практики работы с coding agents
|
|
||||||
|
|
||||||
> По мотивам [Best Practice](https://docs.z.ai/devpack/resources/best-practice) (Z.AI DevPack)
|
|
||||||
|
|
||||||
По мере развития фундаментальных моделей ИИ-инструменты для разработки эволюционируют от простых ассистентов автодополнения кода в **coding agents**, способных участвовать в полном цикле разработки ПО. В отличие от традиционных copilot-инструментов, coding agents умеют читать и навигировать по кодовой базе, модифицировать файлы, выполнять команды, вызывать внешние инструменты и решать сложные задачи через многошаговое взаимодействие.
|
|
||||||
|
|
||||||
С этим сдвигом разработчикам нужно больше, чем техники написания промптов. Нужен надёжный подход к работе с coding agents на практике. Среди ведущих инструментов формируется общий паттерн использования: предоставить чёткий контекст задачи, спланировать шаги выполнения, зафиксировать проектные правила, подключить внешние инструменты и системы, автоматизировать повторяющиеся процессы.
|
|
||||||
|
|
||||||
Опираясь на официальные рекомендации этих инструментов, статья описывает **общий фреймворк лучших практик для coding agents**.
|
|
||||||
|
|
||||||
## 1. Относитесь к агенту как к коллеге, а не к одноразовому инструменту
|
|
||||||
|
|
||||||
Типичная ошибка при работе с coding agent — использовать его как одноразовый вопрос-ответ:
|
|
||||||
|
|
||||||
> Задать вопрос, получить код, завершить взаимодействие.
|
|
||||||
|
|
||||||
На практике этот подход не раскрывает возможности агента.
|
|
||||||
|
|
||||||
Coding agent лучше воспринимать как настраиваемого коллегу, которого можно совершенствовать со временем. Через конфигурационные файлы проекта, интеграции с инструментами и переиспользуемые навыки (skills) разработчик может постоянно формировать поведение агента так, чтобы оно соответствовало рабочему процессу команды.
|
|
||||||
|
|
||||||
!!! tip "Ключевая мысль"
|
|
||||||
Ценность coding agent определяется не только возможностями модели. Она складывается из возможностей модели **и** рабочего процесса вокруг неё.
|
|
||||||
|
|
||||||
## 2. Структурируйте входные данные задачи: контекст важнее промпт-инженерии
|
|
||||||
|
|
||||||
При работе с coding agent многие разработчики слишком фокусируются на технике написания промптов и недостаточно на том, что важнее: **контексте задачи**.
|
|
||||||
|
|
||||||
В сложной кодовой базе эффективное описание задачи обычно включает четыре элемента:
|
|
||||||
|
|
||||||
- **Цель.** Чётко опишите, что нужно сделать: исправить баг, реализовать эндпоинт, отрефакторить модуль
|
|
||||||
- **Контекст.** Укажите релевантные файлы, сообщения об ошибках, документацию или примеры. Назовите конкретные файлы, функции или модули
|
|
||||||
- **Ограничения.** Перечислите инженерные требования: стандарты кодирования, архитектурные правила, требования безопасности, ограничения зависимостей
|
|
||||||
- **Критерии завершения.** Определите, как оценивать готовность: тесты проходят, поведение изменилось ожидаемым образом, баг больше не воспроизводится
|
|
||||||
|
|
||||||
Такой структурированный ввод снижает лишние догадки и делает изменения агента более последовательными и легко проверяемыми.
|
|
||||||
|
|
||||||
В большинстве coding agents контекст можно предоставить, указав на файлы, приложив фрагменты кода или явно описав детали в промпте. Когда контекст задан, следующий шаг для сложной работы: планирование перед внесением изменений.
|
|
||||||
|
|
||||||
## 3. Планируйте перед выполнением сложных задач
|
|
||||||
|
|
||||||
Когда задача имеет чёткий контекст, следующая проблема: выполнение. Для сложных запросов coding agents наиболее эффективны, когда они **планируют перед действием**.
|
|
||||||
|
|
||||||
Если попросить агента сразу писать код при сложном запросе, это часто приводит к логическим ошибкам, ненужной переработке или повторным правкам. Более эффективный подход: **сначала план, потом реализация**.
|
|
||||||
|
|
||||||
Фаза планирования обычно включает:
|
|
||||||
|
|
||||||
- Анализ кодовой базы
|
|
||||||
- Определение объёма изменений
|
|
||||||
- Подтверждение подхода к реализации до начала правок
|
|
||||||
|
|
||||||
Например, Claude Code поощряет шаг анализа и планирования для сложных задач. Некоторые coding agents также предоставляют выделенный режим планирования, который генерирует полный план выполнения перед реализацией.
|
|
||||||
|
|
||||||
Это сдвигает агента от простой генерации кода по запросу к выполнению работы пошагово по явному плану.
|
|
||||||
|
|
||||||
## 4. Фиксируйте повторяющиеся правила в конфигурационных файлах проекта
|
|
||||||
|
|
||||||
На практике многие промпты повторяют одни и те же проектные правила:
|
|
||||||
|
|
||||||
- структура директорий проекта
|
|
||||||
- команды сборки
|
|
||||||
- процесс тестирования
|
|
||||||
- стандарты кодирования
|
|
||||||
- процесс подачи PR
|
|
||||||
|
|
||||||
Если эти правила повторяются в каждом промпте, рабочий процесс становится неэффективным, а инструкции со временем начинают расходиться.
|
|
||||||
|
|
||||||
Поэтому большинство coding agents позволяют хранить **долгоживущие проектные правила** в конфигурационных файлах проекта, чтобы агент автоматически загружал нужный контекст при выполнении задач.
|
|
||||||
|
|
||||||
В одних инструментах это файлы-инструкции для агента, описывающие структуру репозитория, способ запуска проекта и принятые конвенции. В других та же информация фиксируется через конфигурационные файлы, скрипты или настройки проекта.
|
|
||||||
|
|
||||||
Независимо от реализации, цель одна: перенести информацию, которую иначе пришлось бы повторять в диалоге, в **стабильный проектный контекст**.
|
|
||||||
|
|
||||||
!!! success "Практическое правило"
|
|
||||||
**Временные инструкции пишите в промпте, а долгоживущие правила фиксируйте в конфигурационных файлах проекта.**
|
|
||||||
|
|
||||||
## 5. Среда выполнения определяет возможности агента
|
|
||||||
|
|
||||||
Работая с coding agents, разработчики часто объясняют непоследовательные результаты возможностями модели. На практике многие из этих проблем вызваны неполной или неправильно настроенной **средой выполнения**.
|
|
||||||
|
|
||||||
В отличие от традиционных инструментов автодополнения, coding agents обычно работают в реальной среде разработки и выполняют задачи:
|
|
||||||
|
|
||||||
- чтение и модификация исходных файлов
|
|
||||||
- запуск команд сборки или тестирования
|
|
||||||
- вызов внешних инструментов или API
|
|
||||||
- взаимодействие с системами контроля версий
|
|
||||||
|
|
||||||
Поведение агента зависит не только от возможностей модели, но и от того, **насколько среда выполнения полна, стабильна и доступна**. При неправильной конфигурации агент может столкнуться с проблемами:
|
|
||||||
|
|
||||||
!!! warning "Типичные проблемы среды"
|
|
||||||
- Невозможность найти нужную директорию проекта
|
|
||||||
- Отсутствие прав на чтение или модификацию критичных файлов
|
|
||||||
- Невозможность запустить команды сборки или тестирования
|
|
||||||
- Отсутствие доступа к внешним инструментам или сервисам
|
|
||||||
|
|
||||||
Эти проблемы часто выглядят как непонимание со стороны модели или низкое качество кода, но реальная причина обычно в том, что у агента недостаточно прав выполнения или доступа к нужному контексту.
|
|
||||||
|
|
||||||
Большинство ведущих coding agents предоставляют настройки среды:
|
|
||||||
|
|
||||||
- выбор модели или уровня рассуждений
|
|
||||||
- управление правами доступа к файлам и политиками песочницы
|
|
||||||
- определение разрешённых команд
|
|
||||||
- настройка подключений к внешним инструментам или сервисам
|
|
||||||
|
|
||||||
!!! success "Три типа контекста"
|
|
||||||
Coding agent зависит от трёх типов контекста:
|
|
||||||
|
|
||||||
- **Контекст задачи**: промпт и входные данные текущей задачи
|
|
||||||
- **Контекст проекта**: структура репозитория и инженерные правила
|
|
||||||
- **Контекст среды**: инструменты, права доступа и среда выполнения
|
|
||||||
|
|
||||||
Из них контекст среды определяет **что агент может делать и как далеко зайти**.
|
|
||||||
|
|
||||||
## 6. Вовлекайте агента в полный цикл разработки
|
|
||||||
|
|
||||||
Когда у coding agent есть правильная среда выполнения, следующий шаг: вовлечь его в полный цикл разработки, а не использовать только для генерации кода. В реальной разработке изменение кода оценивается не только по генерации. Оно должно пройти тесты, соответствовать инженерным стандартам и пройти ревью.
|
|
||||||
|
|
||||||
Типичный цикл разработки с агентом включает шаги:
|
|
||||||
|
|
||||||
1. **Реализация изменений.** Модификация существующего кода или добавление нового по требованиям задачи
|
|
||||||
2. **Написание или обновление тестов.** Добавление тестового покрытия для новой функциональности или исправляемого бага
|
|
||||||
3. **Запуск тестов.** Выполнение модульных или интеграционных тестов для проверки ожидаемого поведения
|
|
||||||
4. **Проверка кода.** Запуск линтеров, форматирования или проверки типов для соответствия стандартам
|
|
||||||
5. **Ревью изменений.** Инспекция диффа для выявления потенциальных проблем, рисков регрессии или нежелательных модификаций
|
|
||||||
|
|
||||||
В этом рабочем процессе coding agent перестаёт быть просто генератором кода. Он становится активным участником **реализации, валидации и ревью**.
|
|
||||||
|
|
||||||
!!! success "Смена роли"
|
|
||||||
С точки зрения рабочего процесса, coding agent трансформируется из традиционного **генератора кода** в **узел выполнения внутри цикла разработки**.
|
|
||||||
|
|
||||||
## 7. Расширяйте контекст агента через MCP
|
|
||||||
|
|
||||||
В реальных рабочих процессах информация, необходимая coding agent, не всегда находится в репозитории. Многие данные, влияющие на решения при реализации, распределены по внешним системам:
|
|
||||||
|
|
||||||
- системы трекинга задач и требований
|
|
||||||
- статус и результаты CI/CD
|
|
||||||
- схемы баз данных или продуктовые данные
|
|
||||||
- документация API и ссылки на внешние сервисы
|
|
||||||
|
|
||||||
Если эту информацию приходится копировать и вставлять вручную каждый раз, процесс становится неэффективным, а контекст, передаваемый агенту, фрагментирован и ненадёжен.
|
|
||||||
|
|
||||||
Поэтому многие coding agents поддерживают **Model Context Protocol (MCP)**, который предоставляет стандартный способ подключения внешних инструментов и систем. Через MCP coding agent может получать доступ к:
|
|
||||||
|
|
||||||
- платформам хостинга и совместной работы с кодом
|
|
||||||
- базам данных и интерфейсам запросов
|
|
||||||
- API-сервисам и технической документации
|
|
||||||
- внутренним инструментам и системам автоматизации
|
|
||||||
|
|
||||||
!!! success "Расширение границ"
|
|
||||||
Когда агент может работать только с информацией из промпта, он обычно ограничен локальными задачами. Подключение к внешним системам позволяет ему участвовать в более полных рабочих процессах: читать контекст задач, исследовать упавшие CI-запуски, проверять определения API, анализировать проблемы по схемам баз данных.
|
|
||||||
|
|
||||||
Агент эволюционирует из **исполнителя уровня репозитория** в **узел взаимодействия внутри реальной инженерной среды**.
|
|
||||||
|
|
||||||
## 8. Оформляйте повторяющиеся процессы как Skills
|
|
||||||
|
|
||||||
Со временем команды обнаруживают, что определённые задачи возникают снова и снова:
|
|
||||||
|
|
||||||
- ревью PR
|
|
||||||
- анализ логов
|
|
||||||
- генерация release notes
|
|
||||||
- стандартные отладочные процессы
|
|
||||||
|
|
||||||
Если описывать эти задачи вручную в промпте каждый раз, результат: ненужное повторение и менее стабильные результаты.
|
|
||||||
|
|
||||||
Поэтому многие системы coding agents предоставляют механизм **Skills**: упаковку типовых процессов в переиспользуемые шаблоны.
|
|
||||||
|
|
||||||
На высоком уровне Skill можно понимать как **структурированный шаблон рабочего процесса**. Он абстрагирует логику выполнения, которая иначе была бы разбросана по промптам, и позволяет агенту применять один и тот же процесс последовательно при обработке похожих задач.
|
|
||||||
|
|
||||||
Разные инструменты реализуют Skills по-разному: через выделенные файлы, конфигурацию или скрипты. Но цель одна: **превратить разовые промпты в переиспользуемые рабочие процессы**.
|
|
||||||
|
|
||||||
На практике работает простое правило:
|
|
||||||
|
|
||||||
> **Если паттерн промпта или поток задач используется повторно, он, вероятно, должен быть оформлен как Skill.**
|
|
||||||
|
|
||||||
## 9. Автоматизируйте стабильные процессы
|
|
||||||
|
|
||||||
Когда Skill можно выполнить надёжно, следующий шаг: автоматизация.
|
|
||||||
|
|
||||||
В долгоживущих рабочих процессах разработки многие задачи повторяются или привязаны ко времени:
|
|
||||||
|
|
||||||
- генерация резюме коммитов по расписанию
|
|
||||||
- автоматическое расследование упавших CI-запусков
|
|
||||||
- сканирование на потенциальные баги или аномальные логи
|
|
||||||
- подготовка ежедневных или еженедельных инженерных отчётов
|
|
||||||
|
|
||||||
Даже если эти задачи уже оформлены как Skills, они по-прежнему создают ручную работу, если разработчикам приходится запускать их каждый раз.
|
|
||||||
|
|
||||||
!!! info "Автоматизация как следующий слой"
|
|
||||||
Автоматизация находится уровнем выше Skills. Skill определяет **как** выполняется рабочий процесс, а автоматизация определяет **когда** он запускается и **как** продолжает работать со временем.
|
|
||||||
|
|
||||||
Например, навык генерации release notes можно настроить на запуск:
|
|
||||||
|
|
||||||
- при каждой новой публикации релиза
|
|
||||||
- раз в неделю для подготовки сводки релизов
|
|
||||||
- автоматически после завершения CI
|
|
||||||
|
|
||||||
Это сдвигает coding agent из **интерактивного инструмента** в **непрерывного ассистента разработки**.
|
|
||||||
|
|
||||||
## 10. Управляйте сессиями осознанно
|
|
||||||
|
|
||||||
При работе с coding agents сессия — это больше, чем история чата. На практике она функционирует как **рабочий контекст**, который накапливает контекст, промежуточные рассуждения и результаты выполнения.
|
|
||||||
|
|
||||||
По мере продвижения задачи агент постепенно наращивает информацию в рамках той же сессии:
|
|
||||||
|
|
||||||
- цель задачи
|
|
||||||
- релевантный контекст кода
|
|
||||||
- уже внесённые изменения
|
|
||||||
- промежуточные рассуждения и решения
|
|
||||||
|
|
||||||
Если сессиями не управлять, несвязанные задачи накапливаются в одной сессии, делая контекст излишне сложным. Это часто снижает качество рассуждений и выполнения агента.
|
|
||||||
|
|
||||||
Общие практики:
|
|
||||||
|
|
||||||
- **Используйте отдельную сессию для каждой задачи.** Не смешивайте несвязанные задачи, чтобы рабочий контекст оставался ясным
|
|
||||||
- **Избегайте слишком длинных сессий.** Когда сессия накапливает слишком много истории, используйте резюме или сжатие для снижения нагрузки на контекст
|
|
||||||
- **Начинайте новую сессию для ответвлений.** Если задача открывает новое направление исследования, продолжайте его в отдельной сессии
|
|
||||||
- **Периодически сжимайте исторический контекст.** Резюмируйте старые части разговора для снижения давления на контекстное окно
|
|
||||||
|
|
||||||
В более сложных сценариях команды могут использовать **модель многоагентного сотрудничества**: подзадачи (исследование кодовой базы, запуск тестов, расследование сбоев) делегируются отдельным агентам, а главный агент координирует общую задачу. Это сохраняет ясность основной сессии и повышает эффективность выполнения.
|
|
||||||
|
|
||||||
## Заключение
|
|
||||||
|
|
||||||
Эффективность coding agent определяется не только моделью. Она зависит от того, как разработчики выстраивают рабочий процесс вокруг неё.
|
|
||||||
|
|
||||||
Зрелый рабочий процесс с coding agent обычно включает следующие этапы:
|
|
||||||
|
|
||||||
1. Структурированный ввод задачи с контекстом
|
|
||||||
2. Планирование перед выполнением
|
|
||||||
3. Проектные правила в конфигурационных файлах
|
|
||||||
4. Настроенная среда выполнения
|
|
||||||
5. Участие в полном цикле разработки
|
|
||||||
6. Расширение контекста через MCP
|
|
||||||
7. Повторяющиеся процессы как Skills
|
|
||||||
8. Автоматизация стабильных процессов
|
|
||||||
9. Осознанное управление сессиями
|
|
||||||
@@ -1,362 +0,0 @@
|
|||||||
# Механизм памяти coding agents
|
|
||||||
|
|
||||||
> По мотивам [Memory Mechanism](https://docs.z.ai/devpack/resources/memory-mechanism) (Z.AI DevPack)
|
|
||||||
|
|
||||||
Память позволяет coding agent сохранять контекст между задачами и сессиями, сокращая повторный ввод и повышая эффективность выполнения. С хорошо продуманной системой памяти агент может постоянно учитывать структуру проекта, инженерные конвенции и предпочтения пользователя, автоматически переиспользуя эту информацию в будущей работе.
|
|
||||||
|
|
||||||
В системах coding agents память обычно организована в несколько слоёв: **автоматическая память, проектная память** и **сессионная память**.
|
|
||||||
|
|
||||||
## Зачем coding agents нужна память?
|
|
||||||
|
|
||||||
Традиционные большие языковые модели не сохраняют состояние между вызовами. Они не могут запомнить контекст проекта между сессиями, накапливать опыт решения проблем или последовательно адаптироваться к предпочтениям пользователя.
|
|
||||||
|
|
||||||
Агентные системы решают это ограничение через **внешнюю память**.
|
|
||||||
|
|
||||||
Типичная архитектура выглядит так:
|
|
||||||
|
|
||||||
```
|
|
||||||
Ввод пользователя
|
|
||||||
↓
|
|
||||||
Извлечение памяти
|
|
||||||
↓
|
|
||||||
Сборка контекста
|
|
||||||
↓
|
|
||||||
Рассуждение LLM
|
|
||||||
↓
|
|
||||||
Действие / вызов инструмента
|
|
||||||
↓
|
|
||||||
Обновление памяти
|
|
||||||
```
|
|
||||||
|
|
||||||
Агент извлекает релевантную память перед началом задачи и обновляет память после завершения.
|
|
||||||
|
|
||||||
Эта архитектура является общим паттерном в современных агентных системах, таких как LangGraph, AutoGPT и Devin.
|
|
||||||
|
|
||||||
## Полная архитектура памяти
|
|
||||||
|
|
||||||
На высоком уровне полная архитектура памяти агента выглядит так:
|
|
||||||
|
|
||||||
```
|
|
||||||
Краткосрочная память
|
|
||||||
↓
|
|
||||||
Контекст сессии
|
|
||||||
|
|
||||||
Долгосрочная память
|
|
||||||
├ семантическая память
|
|
||||||
├ эпизодическая память
|
|
||||||
└ процедурная память
|
|
||||||
```
|
|
||||||
|
|
||||||
## Основные типы памяти
|
|
||||||
|
|
||||||
### Сессионная память
|
|
||||||
|
|
||||||
Сессионная память — это контекстная информация текущей задачи. Включает текущую историю разговора, последние результаты инструментов, текущий план выполнения и содержимое файлов в области видимости. Эта информация обычно находится в контекстном окне модели.
|
|
||||||
|
|
||||||
Пример:
|
|
||||||
|
|
||||||
```
|
|
||||||
Пользователь: Исправь этот баг в Python
|
|
||||||
Агент: Анализирует ошибку
|
|
||||||
Агент: Модифицирует код
|
|
||||||
Агент: Запускает тесты
|
|
||||||
```
|
|
||||||
|
|
||||||
Все эти шаги выполнения относятся к сессионной памяти.
|
|
||||||
|
|
||||||
### Проектная память
|
|
||||||
|
|
||||||
Проектная память хранит **долгоживущую информацию о всей кодовой базе**: архитектуру проекта, стандарты кодирования, процессы сборки, часто используемые команды. Такая память обычно записывается в `.md`-файлы и загружается в начале сессии.
|
|
||||||
|
|
||||||
Пример структуры:
|
|
||||||
|
|
||||||
```
|
|
||||||
your-project/
|
|
||||||
├── .claude/
|
|
||||||
│ ├── CLAUDE.md # Основные инструкции проекта
|
|
||||||
│ └── rules/
|
|
||||||
│ ├── code-style.md # Стиль кода
|
|
||||||
│ ├── testing.md # Конвенции тестирования
|
|
||||||
│ └── security.md # Требования безопасности
|
|
||||||
```
|
|
||||||
|
|
||||||
При такой структуре агент автоматически следует этим правилам при модификации кода.
|
|
||||||
|
|
||||||
### Семантическая память
|
|
||||||
|
|
||||||
Семантическая память хранит фактические знания и справочную информацию: документацию API, правила языков программирования, базы знаний проекта. На практике часто реализуется через RAG (Retrieval-Augmented Generation).
|
|
||||||
|
|
||||||
Типичный поток:
|
|
||||||
|
|
||||||
```
|
|
||||||
запрос
|
|
||||||
↓
|
|
||||||
эмбеддинг
|
|
||||||
↓
|
|
||||||
векторный поиск
|
|
||||||
↓
|
|
||||||
извлечение документов
|
|
||||||
↓
|
|
||||||
рассуждение LLM
|
|
||||||
```
|
|
||||||
|
|
||||||
Это один из наиболее распространённых методов запоминания в coding agents.
|
|
||||||
|
|
||||||
### Эпизодическая память
|
|
||||||
|
|
||||||
Эпизодическая память записывает прошлый опыт агента: шаги исправления предыдущего бага, корневую причину прошлого сбоя сборки, стратегию отладки, которая сработала. Этот тип памяти помогает агенту учиться на предыдущем опыте.
|
|
||||||
|
|
||||||
Пример:
|
|
||||||
|
|
||||||
```
|
|
||||||
Эпизод:
|
|
||||||
Сбой CI из-за отсутствующей зависимости
|
|
||||||
Решение: обновить pip-пакет
|
|
||||||
```
|
|
||||||
|
|
||||||
### Процедурная память
|
|
||||||
|
|
||||||
Процедурная память хранит стратегии или пошаговые процессы выполнения задач.
|
|
||||||
|
|
||||||
Пример:
|
|
||||||
|
|
||||||
```
|
|
||||||
Debug_Workflow.md
|
|
||||||
1. прочитать лог ошибок
|
|
||||||
2. найти файл
|
|
||||||
3. написать патч
|
|
||||||
4. запустить тесты
|
|
||||||
```
|
|
||||||
|
|
||||||
Такая память обычно используется в системных промптах, шаблонах рабочих процессов и политиках агента.
|
|
||||||
|
|
||||||
## Стандартный паттерн использования памяти
|
|
||||||
|
|
||||||
В реальных системах агенты обычно следуют единообразному процессу работы с памятью.
|
|
||||||
|
|
||||||
**Шаг 1: Извлечение памяти**
|
|
||||||
|
|
||||||
Перед началом задачи агент извлекает релевантную проектную память, записи из базы знаний и предыдущий опыт, затем внедряет их в рабочий контекст.
|
|
||||||
|
|
||||||
**Шаг 2: Сборка контекста**
|
|
||||||
|
|
||||||
Извлечённые воспоминания собираются в полный контекст и передаются модели.
|
|
||||||
|
|
||||||
**Шаг 3: Обновление памяти**
|
|
||||||
|
|
||||||
После завершения задачи агент решает, нужно ли записать новые воспоминания: обнаруженные проектные правила, опыт отладки или предпочтения пользователя.
|
|
||||||
|
|
||||||
## Как правильно использовать память
|
|
||||||
|
|
||||||
В основных агентных системах память проектируется как **многослойная, управляемая, извлекаемая и обновляемая**.
|
|
||||||
|
|
||||||
Обычно память делится на **краткосрочную** и **долгосрочную**. Краткосрочная используется для сохранения состояния в текущем потоке или сессии. Долгосрочная поддерживается через явные файлы, конфигурации правил, векторное извлечение или другие механизмы постоянного хранения.
|
|
||||||
|
|
||||||
Например, в **Claude Code** каждая сессия начинается с чистого контекстного окна. Знания переносятся между сессиями через файлы инструкций (CLAUDE.md) и **автоматическую память**. В **LangChain / LangGraph** память также делится на **краткосрочную в рамках потока** и **долгосрочную между сессиями**.
|
|
||||||
|
|
||||||
На практике наиболее эффективный подход: не полагаться на модель в автоматическом «запоминании всего», а установить чёткий паттерн управления памятью. Определить: что записывать в проектные файлы памяти, что извлекать из базы знаний или векторного хранилища, что оставить только в текущей сессии, а что продвинуть в долгосрочную память после завершения задачи.
|
|
||||||
|
|
||||||
### Разделяйте инструкционную и обучающую память
|
|
||||||
|
|
||||||
Один из наиболее практичных принципов: различать два фундаментально разных вида памяти.
|
|
||||||
|
|
||||||
- **Инструкционная память**: написана людьми, чтобы указать агенту, как он должен работать. Обычно включает стандарты кодирования, конвенции директорий, команды сборки, процедуры тестирования, требования к именованию, правила коммитов и правила безопасности на уровне команды. В Claude Code это файлы инструкций вроде `CLAUDE.md`
|
|
||||||
|
|
||||||
- **Обучающая память**: не определена заранее, а накоплена агентом из ваших поправок, предпочтений, неудачных попыток, частых команд и привычек проекта. В Claude Code это называется автоматическая память (auto memory)
|
|
||||||
|
|
||||||
Если эти два типа памяти смешиваются, поведение системы со временем дрейфует. Лучший подход: чётко разделить их роли.
|
|
||||||
|
|
||||||
- **Правила, политики и поведенческие ограничения** записывайте в **инструкционную память**, чтобы поведение агента оставалось стабильным и предсказуемым
|
|
||||||
- **Опыт, предпочтения пользователя, временные открытия и ретроспективные выводы** записывайте в **обучающую память**, чтобы решения улучшались в будущих задачах
|
|
||||||
|
|
||||||
Это разделение предотвращает постепенное загрязнение основных правил системы заметками из опыта.
|
|
||||||
|
|
||||||
### Многоуровневое управление памятью
|
|
||||||
|
|
||||||
#### Уровень организации
|
|
||||||
|
|
||||||
Правила, определённые и распространяемые на уровне команды или компании, применимые ко всем разработчикам и проектам:
|
|
||||||
|
|
||||||
- требования безопасности и соответствия
|
|
||||||
- базовые стандарты код-ревью
|
|
||||||
- запрещённые директории для чтения/записи
|
|
||||||
- ограничения зависимостей и лицензий
|
|
||||||
- инженерные стандарты организации
|
|
||||||
|
|
||||||
На организационном уровне общий файл правил развёртывается по системному пути и не должен легко отключаться пользователями. **Организационная память — это высокоприоритетный управленческий слой, который не должен обходиться.**
|
|
||||||
|
|
||||||
#### Уровень проекта
|
|
||||||
|
|
||||||
Командный контекст проекта, версионируемый и общий для всех участников. **Это самый важный слой памяти для coding agent.**
|
|
||||||
|
|
||||||
- документация архитектуры проекта
|
|
||||||
- конвенции структуры директорий
|
|
||||||
- команды сборки и тестирования
|
|
||||||
- где должны располагаться API
|
|
||||||
- конвенции именования
|
|
||||||
- типовые процессы разработки
|
|
||||||
|
|
||||||
Claude Code рекомендует хранить эту информацию в проектном файле, а команда `/init` может автоматически сгенерировать первоначальный черновик. Ключевое свойство этого слоя: **общий для проекта, под контролем версий, стабильный во времени**.
|
|
||||||
|
|
||||||
#### Уровень пользователя
|
|
||||||
|
|
||||||
Персональные предпочтения разработчика, применимые ко всем проектам. Лучше хранить в домашней директории пользователя как переиспользуемый личный контекст для всех рабочих пространств:
|
|
||||||
|
|
||||||
- предпочитаемый стиль кодирования
|
|
||||||
- привычная последовательность отладки
|
|
||||||
- предпочитаемый формат вывода
|
|
||||||
- персональные быстрые команды
|
|
||||||
|
|
||||||
Должен дополнять проектные конвенции, а не переопределять их.
|
|
||||||
|
|
||||||
#### Локальный уровень
|
|
||||||
|
|
||||||
Специфичен для вашей локальной копии проекта, **не должен попадать в Git**:
|
|
||||||
|
|
||||||
- персональные тестовые аккаунты
|
|
||||||
- локальные порты разработки
|
|
||||||
- временные адреса тестовых заглушек
|
|
||||||
- заметки по среде выполнения на конкретной машине
|
|
||||||
- экспериментальные рабочие процессы, не готовые к распространению
|
|
||||||
|
|
||||||
Ценность этого слоя: **позволяет индивидуальную эффективную работу без загрязнения общей памяти**.
|
|
||||||
|
|
||||||
#### Уровень субагента / роли
|
|
||||||
|
|
||||||
Разные субагенты могут поддерживать собственные области памяти вместо использования единой глобальной. Это особенно важно в многоагентных системах, где одна из самых частых проблем: загрязнение контекста между ролями.
|
|
||||||
|
|
||||||
Лучший паттерн: каждый субагент хранит только память, релевантную его роли:
|
|
||||||
|
|
||||||
- **агент тестирования** помнит команды тестирования, поведение CI, стиль утверждений
|
|
||||||
- **агент рефакторинга** помнит границы модулей, запрещённые зависимости, стратегии миграции
|
|
||||||
- **агент документации** помнит глоссарий терминов, шаблоны документации, стиль для целевой аудитории
|
|
||||||
|
|
||||||
Это делает память короче, точнее и стабильнее.
|
|
||||||
|
|
||||||
### Загрузка `.md`-файлов по пути
|
|
||||||
|
|
||||||
Для крупных репозиториев рекомендуется разделять инструкции на несколько Markdown-файлов в `.claude/rules/`, каждый посвящён одной теме: `testing.md`, `api-design.md`, `security.md`.
|
|
||||||
|
|
||||||
Claude Code также поддерживает **привязку правил к определённым поддиректориям или типам файлов**: правила загружаются только когда агент работает с подходящими файлами. Это снижает шум и экономит контекстное окно.
|
|
||||||
|
|
||||||
Три принципа организации:
|
|
||||||
|
|
||||||
- **Основной файл памяти ограничен глобальным общим контекстом**: фон проекта, высокоуровневая архитектура, кросс-проектные конвенции
|
|
||||||
- **Специализированные правила модульны**: один файл правил на тему
|
|
||||||
- **Если правило можно загрузить по пути, не загружайте его глобально**: включайте в контекст только при необходимости
|
|
||||||
|
|
||||||
Пример структуры:
|
|
||||||
|
|
||||||
```
|
|
||||||
agent-memory/
|
|
||||||
├── project.md # Обзор проекта
|
|
||||||
├── rules/
|
|
||||||
│ ├── code-style.md # Стиль кода
|
|
||||||
│ ├── testing.md # Конвенции тестирования
|
|
||||||
│ ├── api-design.md # Правила дизайна API
|
|
||||||
│ ├── security.md # Требования безопасности
|
|
||||||
│ └── frontend/
|
|
||||||
│ └── react.md # Правила фронтенда
|
|
||||||
└── local/
|
|
||||||
└── developer.local.md
|
|
||||||
```
|
|
||||||
|
|
||||||
Три преимущества такой структуры:
|
|
||||||
|
|
||||||
1. **Проще поддерживать.** Каждый файл правил фокусируется на одной теме, набор правил менее склонен к разрастанию
|
|
||||||
2. **Проще загружать по запросу.** Когда агент работает над тестами, ему не нужно загружать конвенции фронтенда или правила баз данных
|
|
||||||
3. **Лучше для командной работы.** Разные команды могут поддерживать собственные директории правил вместо редактирования единого монолитного файла
|
|
||||||
|
|
||||||
### Пишите правила памяти как конкретные инструкции
|
|
||||||
|
|
||||||
При написании памяти агента используйте **конкретные, проверяемые правила**, а не абстрактные принципы. Чем яснее инструкции, тем стабильнее поведение агента.
|
|
||||||
|
|
||||||
Общие рекомендации:
|
|
||||||
|
|
||||||
- инструкции должны быть **лаконичными и явными**
|
|
||||||
- правила должны быть **согласованы** друг с другом
|
|
||||||
- основной файл памяти **не более 200 строк** по возможности
|
|
||||||
- используйте **Markdown-заголовки и списки** для читаемости
|
|
||||||
- формулируйте требования как правила, которые можно **проверить и выполнить**
|
|
||||||
|
|
||||||
Избегайте расплывчатых формулировок:
|
|
||||||
|
|
||||||
- ~~Держите код чистым~~
|
|
||||||
- ~~Пишите хорошие тесты~~
|
|
||||||
- ~~Следите за дизайном API~~
|
|
||||||
- ~~Разделяйте модули при необходимости~~
|
|
||||||
|
|
||||||
Предпочитайте конкретные правила:
|
|
||||||
|
|
||||||
- Используйте **2-пробельный отступ** во всех новых TypeScript-файлах
|
|
||||||
- **Запускайте `pnpm test`** после модификации бизнес-логики
|
|
||||||
- Размещайте **все обработчики API в `src/api/handlers/`**
|
|
||||||
- Держите React-компоненты страниц **менее 300 строк**; разбивайте большие на хуки или дочерние компоненты
|
|
||||||
|
|
||||||
Конкретные правила значительно сокращают пространство для интерпретации агентом, что повышает стабильность поведения.
|
|
||||||
|
|
||||||
### Переиспользование памяти через импорт
|
|
||||||
|
|
||||||
В реальных проектах многие правила — это **общие инженерные конвенции между репозиториями**. Переписывание их в каждом репозитории увеличивает накладные расходы на поддержку и повышает вероятность рассогласования.
|
|
||||||
|
|
||||||
В Claude Code:
|
|
||||||
|
|
||||||
- `CLAUDE.md` может импортировать другие файлы правил через `@path/to/import`
|
|
||||||
- `.claude/rules/` может делить правила через **символические ссылки** (symlinks)
|
|
||||||
- импортируемый контент раскрывается **рекурсивно**, символические ссылки разрешаются нормально
|
|
||||||
|
|
||||||
Это позволяет командам создавать **переиспользуемые пакеты правил**:
|
|
||||||
|
|
||||||
- `company-security-rules`
|
|
||||||
- `frontend-react-rules`
|
|
||||||
- `backend-api-rules`
|
|
||||||
- `python-testing-rules`
|
|
||||||
|
|
||||||
Каждый проект ссылается только на нужные модули правил, а не поддерживает полную копию всего набора.
|
|
||||||
|
|
||||||
Два прямых преимущества:
|
|
||||||
|
|
||||||
1. **Правила поддерживаются централизованно и обновляются единообразно**
|
|
||||||
2. **Разные проекты разделяют один инженерный язык**, делая поведение агента согласованным между репозиториями
|
|
||||||
|
|
||||||
## Устранение проблем с памятью
|
|
||||||
|
|
||||||
### Агент не следует `.md`-файлам памяти
|
|
||||||
|
|
||||||
`.md`-файлы памяти предоставляются агенту как контекстные инструкции, а не как принудительная конфигурация. Агент прочитает их и попытается следовать, но не гарантирует строгое соблюдение при расплывчатых, неясных или конфликтующих правилах.
|
|
||||||
|
|
||||||
Если агент не следует правилам, проверьте:
|
|
||||||
|
|
||||||
- Подтвердите загрузку `.md`-файлов памяти (команда `/memory` или аналог)
|
|
||||||
- Проверьте, находятся ли файлы в пути, разрешённом для загрузки в текущей сессии
|
|
||||||
- Проверьте конфликты правил между файлами. Если разные файлы дают разные инструкции для одного поведения, агент может выбрать произвольно
|
|
||||||
|
|
||||||
### Непонятно, что сохранила автоматическая память
|
|
||||||
|
|
||||||
Большинство coding agents поддерживают авто-память в фоне для захвата контекста проекта, предпочтений пользователя или частых действий.
|
|
||||||
|
|
||||||
Способы проверки:
|
|
||||||
|
|
||||||
- Выполните `/memory` (или аналогичную команду) для просмотра текущей директории авто-памяти
|
|
||||||
- Авто-память обычно хранится в Markdown-файлах, которые можно читать, редактировать или удалять напрямую
|
|
||||||
|
|
||||||
### Файлы памяти слишком большие
|
|
||||||
|
|
||||||
Раздутые файлы памяти потребляют больше контекстного окна, снижают следование инструкциям и увеличивают вероятность конфликтов.
|
|
||||||
|
|
||||||
Рекомендуется:
|
|
||||||
|
|
||||||
- разделить детальный контент на несколько Markdown-файлов
|
|
||||||
- использовать ссылки на файлы или импорты (`@path/to/file`)
|
|
||||||
- перенести правила в выделенную директорию правил (`rules/`)
|
|
||||||
|
|
||||||
### Инструкции исчезают после сжатия контекста
|
|
||||||
|
|
||||||
Многие coding agents **сжимают или резюмируют контекст** в длинных разговорах для уменьшения длины контекста.
|
|
||||||
|
|
||||||
В большинстве случаев файлы памяти **перезагружаются с диска** после сжатия, поэтому сохраняется только контент, записанный в файлы памяти. Если правила исчезают после сжатия, значит они **существовали только в разговоре** и не были записаны в файл.
|
|
||||||
|
|
||||||
Решение:
|
|
||||||
|
|
||||||
- записывайте долгосрочные инструкции в `.md`-файлы памяти
|
|
||||||
- не полагайтесь только на разговор для сохранения правил
|
|
||||||
+12
-13
@@ -126,18 +126,17 @@ erDiagram
|
|||||||
|
|
||||||
Чуть менее «сказочно», чуть более технично.
|
Чуть менее «сказочно», чуть более технично.
|
||||||
|
|
||||||
### 3.1. Hub: сущность и её бизнес-ключ
|
### 3.1. Hub — сущность и её бизнес-ключ
|
||||||
|
|
||||||
**Hub** содержит:
|
**Hub** содержит:
|
||||||
|
|
||||||
* бизнес‑ключ (customer_bk, order_id, contract_number);
|
* бизнес‑ключ (customer_bk, order_id, contract_number);
|
||||||
* техническую информацию:
|
* техническую информацию:
|
||||||
* record_source — из какой системы пришла первая запись;
|
* record_source — из какой системы пришла первая запись;
|
||||||
* load_dttm — когда запись попала в DV;
|
* load_dttm — когда запись попала в DV;
|
||||||
* иногда — хэш бизнес‑ключа (hk_customer).
|
* иногда — хэш бизнес‑ключа (hk_customer).
|
||||||
|
|
||||||
Главные правила:
|
Главные правила:
|
||||||
|
|
||||||
* один бизнес‑ключ — один хаб (одна строка на сущность, без истории);
|
* один бизнес‑ключ — один хаб (одна строка на сущность, без истории);
|
||||||
* хаб не знает про атрибуты (имя, email) — только идентичность.
|
* хаб не знает про атрибуты (имя, email) — только идентичность.
|
||||||
|
|
||||||
@@ -154,7 +153,7 @@ CREATE TABLE hub_customer (
|
|||||||
|
|
||||||
Конкретные типы данных (`BYTEA`, длины `VARCHAR`, детали `hashdiff`) и реализации хэш‑ключей можно не запоминать: на старте важнее понять саму идею — у сущностей есть стабильные ключи, а все изменения атрибутов мы записываем отдельными версиями в сателлитах.
|
Конкретные типы данных (`BYTEA`, длины `VARCHAR`, детали `hashdiff`) и реализации хэш‑ключей можно не запоминать: на старте важнее понять саму идею — у сущностей есть стабильные ключи, а все изменения атрибутов мы записываем отдельными версиями в сателлитах.
|
||||||
|
|
||||||
### 3.2. Link: связи между сущностями
|
### 3.2. Link — связи между сущностями
|
||||||
|
|
||||||
**Link** описывает факт связи, например:
|
**Link** описывает факт связи, например:
|
||||||
|
|
||||||
@@ -179,7 +178,7 @@ CREATE TABLE link_order_customer (
|
|||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.3. Satellite: атрибуты и история
|
### 3.3. Satellite — атрибуты и история
|
||||||
|
|
||||||
**Satellite** хранит:
|
**Satellite** хранит:
|
||||||
|
|
||||||
@@ -250,7 +249,7 @@ Star Schema]
|
|||||||
* **Raw Vault** — это про приём и хранение данных «как есть», но уже в форме Hub / Link / Satellite.
|
* **Raw Vault** — это про приём и хранение данных «как есть», но уже в форме Hub / Link / Satellite.
|
||||||
* **Business Vault** — это про приведение этих данных в более «деловой» вид: с бизнес-правилами, PIT/Bridge и подготовленными представлениями.
|
* **Business Vault** — это про приведение этих данных в более «деловой» вид: с бизнес-правилами, PIT/Bridge и подготовленными представлениями.
|
||||||
|
|
||||||
### 5.1. Raw Vault: «всё прилетевшее, аккуратно разложенное по ящичкам»
|
### 5.1. Raw Vault — «всё прилетевшее, аккуратно разложенное по ящичкам»
|
||||||
|
|
||||||
Raw DV — первый слой поверх STG / ODS:
|
Raw DV — первый слой поверх STG / ODS:
|
||||||
|
|
||||||
@@ -262,20 +261,20 @@ Raw DV — первый слой поверх STG / ODS:
|
|||||||
|
|
||||||
* минимум бизнес-логики:
|
* минимум бизнес-логики:
|
||||||
|
|
||||||
* никаких правил вроде «клиент активен, если была хотя бы одна покупка за 90 дней»;
|
* никаких правил вроде «клиент активен, если была хотя бы одна покупка за 90 дней»;
|
||||||
* все источники показываются «как есть», только приведены к общим ключам;
|
* все источники показываются «как есть», только приведены к общим ключам;
|
||||||
* структура стабильна: добавился новый источник → появился новый Satellite к тому же Hub.
|
* структура стабильна: добавился новый источник → появился новый Satellite к тому же Hub.
|
||||||
|
|
||||||
### 5.2. Business Vault: «там, где из Lego собирают модули»
|
### 5.2. Business Vault — «там, где из Lego собирают модули»
|
||||||
|
|
||||||
Business Vault (BV) — следующий слой над Raw DV:
|
Business Vault (BV) — следующий слой над Raw DV:
|
||||||
|
|
||||||
* здесь применяются бизнес-правила (что считать активным клиентом, как трактовать статусы);
|
* здесь применяются бизнес-правила (что считать активным клиентом, как трактовать статусы);
|
||||||
* здесь строятся вспомогательные структуры:
|
* здесь строятся вспомогательные структуры:
|
||||||
|
|
||||||
* PIT-таблицы,
|
* PIT-таблицы,
|
||||||
* Bridge-таблицы,
|
* Bridge-таблицы,
|
||||||
* агрегаты и derived-таблицы.
|
* агрегаты и derived-таблицы.
|
||||||
|
|
||||||
Именно из BV чаще всего строятся витрины в формате Звезды, к которым подключаются BI и отчётность.
|
Именно из BV чаще всего строятся витрины в формате Звезды, к которым подключаются BI и отчётность.
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
Структура файла:
|
Структура файла:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
customer_id,status,event_ts,_load_id,_load_ts
|
customer_id,status,event_ts,_load_id,load_ts
|
||||||
101,new,2024-01-01 09:00:00,batch_20240101_1000,2024-01-01 10:00:00
|
101,new,2024-01-01 09:00:00,batch_20240101_1000,2024-01-01 10:00:00
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
@@ -41,7 +41,7 @@ customer_id,status,event_ts,_load_id,_load_ts
|
|||||||
- `status` — статус клиента в CRM (`new`, `active`, `vip`, `churned`);
|
- `status` — статус клиента в CRM (`new`, `active`, `vip`, `churned`);
|
||||||
- `event_ts` — момент, когда статус сменился в CRM;
|
- `event_ts` — момент, когда статус сменился в CRM;
|
||||||
- `_load_id` — идентификатор батча загрузки;
|
- `_load_id` — идентификатор батча загрузки;
|
||||||
- `_load_ts` — момент, когда данные попали в DWH.
|
- `load_ts` — момент, когда данные попали в DWH (в таблицах STG/ODS эта колонка будет называться `_load_ts`, но по смыслу это то же самое время загрузки).
|
||||||
|
|
||||||
Файл содержит несколько клиентов и несколько смен статуса по каждому — этого достаточно, чтобы отработать SCD2.
|
Файл содержит несколько клиентов и несколько смен статуса по каждому — этого достаточно, чтобы отработать SCD2.
|
||||||
|
|
||||||
@@ -52,9 +52,9 @@ customer_id,status,event_ts,_load_id,_load_ts
|
|||||||
Чтобы не тратить время на DDL, структуры таблиц для домашки уже подготовлены в `dwh-modeling/sql`:
|
Чтобы не тратить время на DDL, структуры таблиц для домашки уже подготовлены в `dwh-modeling/sql`:
|
||||||
|
|
||||||
- `07_ddl_hw_customer_status.sql` — создаёт дополнительные таблицы:
|
- `07_ddl_hw_customer_status.sql` — создаёт дополнительные таблицы:
|
||||||
- `stg.customer_status_raw` — сырые события о статусе клиента;
|
- `stg.customer_status_raw` — сырые события о статусе клиента;
|
||||||
- `ods.customer_status` — очищенные и типизированные события;
|
- `ods.customer_status` — очищенные и типизированные события;
|
||||||
- `dds.dim_customer_status` — измерение статусов клиента в формате **SCD Type 2**.
|
- `dds.dim_customer_status` — измерение статусов клиента в формате **SCD Type 2**.
|
||||||
- `08_dml_hw_customer_status_template.sql` — шаблон DML-скрипта с подсказками и заготовками блоков.
|
- `08_dml_hw_customer_status_template.sql` — шаблон DML-скрипта с подсказками и заготовками блоков.
|
||||||
|
|
||||||
Перед началом работы:
|
Перед началом работы:
|
||||||
@@ -62,16 +62,15 @@ customer_id,status,event_ts,_load_id,_load_ts
|
|||||||
1. Поднимите demo‑Postgres по инструкции из корневого `README.md`.
|
1. Поднимите demo‑Postgres по инструкции из корневого `README.md`.
|
||||||
2. Выполните базовые скрипты DWH:
|
2. Выполните базовые скрипты DWH:
|
||||||
- `01_ddl_stg-dds.sql`
|
- `01_ddl_stg-dds.sql`
|
||||||
- `02_dml_stg-dds.sql` (нужен как минимум для `dds.dim_date`)
|
- `02_dml_stg-dds.sql`
|
||||||
- `05_ddl_dm.sql` (создаёт схему `dm` для витрин)
|
|
||||||
3. Выполните DDL для домашки:
|
3. Выполните DDL для домашки:
|
||||||
- `07_ddl_hw_customer_status.sql`
|
- `07_ddl_hw_customer_status.sql`
|
||||||
|
|
||||||
После этого схемы `stg`, `ods`, `dds`, `dm` уже существуют, а дополнительные таблицы для статусов созданы.
|
После этого схемы `stg`, `ods`, `dds` уже существуют, а дополнительные таблицы для статусов созданы.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Часть 1: STG → ODS (обязательно)
|
## 3. Часть 1 — STG → ODS (обязательно)
|
||||||
|
|
||||||
**Задача:** загрузить CSV в STG и переложить данные в ODS с приведением типов.
|
**Задача:** загрузить CSV в STG и переложить данные в ODS с приведением типов.
|
||||||
|
|
||||||
@@ -95,13 +94,13 @@ INSERT INTO stg.customer_status_raw (customer_id, status, event_ts, _load_id, _l
|
|||||||
('101','churned','2024-09-01 12:15:00','batch_20240901_1300','2024-09-01 13:00:00');
|
('101','churned','2024-09-01 12:15:00','batch_20240901_1300','2024-09-01 13:00:00');
|
||||||
```
|
```
|
||||||
|
|
||||||
> 💡 Здесь `_load_ts` — это время загрузки.
|
> 💡 Здесь `_load_ts` — это время загрузки (в CSV оно называется `load_ts`).
|
||||||
|
|
||||||
#### Вариант B: загрузить CSV
|
#### Вариант B: загрузить CSV
|
||||||
|
|
||||||
Можно загрузить файл `dwh-modeling/data/customer_status_events.csv` в таблицу `stg.customer_status_raw`:
|
Можно загрузить файл `dwh-modeling/data/customer_status_events.csv` в таблицу `stg.customer_status_raw`:
|
||||||
|
|
||||||
- **Через DBeaver**: Import Data → CSV → `stg.customer_status_raw`.
|
- **Через DBeaver**: Import Data → CSV → `stg.customer_status_raw` (колонку `load_ts` маппить в `_load_ts`).
|
||||||
- **Через `psql` в контейнере (`./psql_sh`)**: без установки `psql` на хост.
|
- **Через `psql` в контейнере (`./psql_sh`)**: без установки `psql` на хост.
|
||||||
|
|
||||||
Способ: передайте CSV в `psql` через STDIN и выполните `\copy ... FROM STDIN`:
|
Способ: передайте CSV в `psql` через STDIN и выполните `\copy ... FROM STDIN`:
|
||||||
@@ -122,14 +121,12 @@ SELECT * FROM stg.customer_status_raw LIMIT 10;
|
|||||||
|
|
||||||
### 3.2. ODS: очистка и типизация
|
### 3.2. ODS: очистка и типизация
|
||||||
|
|
||||||
> 💡 Обратите внимание: в основном примере `ods.customers` хранит **снимок** (одна строка на клиента, PK = `customer_id`), а здесь `ods.customer_status` хранит **все события** (PK = `customer_id + event_ts`). Это не ошибка, а сознательный выбор: источник данных о статусах - поток событий, и ODS сохраняет эту природу. Подробнее - в комментариях к решению.
|
|
||||||
|
|
||||||
В файле `08_dml_hw_customer_status_template.sql` найдите заготовку блока ODS и допишите SQL:
|
В файле `08_dml_hw_customer_status_template.sql` найдите заготовку блока ODS и допишите SQL:
|
||||||
|
|
||||||
- привести:
|
- привести:
|
||||||
- `customer_id` → `INT`,
|
- `customer_id` → `INT`,
|
||||||
- `status` → `VARCHAR(20)` (можно оставить как есть),
|
- `status` → `VARCHAR(20)` (можно оставить как есть),
|
||||||
- `event_ts` и `_load_ts` → `TIMESTAMP`;
|
- `event_ts` и `load_ts` → `TIMESTAMP` (в DWH-таблицах эта колонка будет лежать как `_load_ts`);
|
||||||
- аккуратно обработать возможные пустые значения (если бы они были);
|
- аккуратно обработать возможные пустые значения (если бы они были);
|
||||||
- заполнить `_load_id` и `_load_ts` в `ods.customer_status`.
|
- заполнить `_load_id` и `_load_ts` в `ods.customer_status`.
|
||||||
|
|
||||||
@@ -145,7 +142,7 @@ ORDER BY customer_id, event_ts;
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Часть 2: ODS → DDS (SCD Type 2, обязательно)
|
## 4. Часть 2 — ODS → DDS (SCD Type 2, обязательно)
|
||||||
|
|
||||||
**Задача:** по событиям в `ods.customer_status` построить измерение `dds.dim_customer_status`, где каждая строка — период действия статуса.
|
**Задача:** по событиям в `ods.customer_status` построить измерение `dds.dim_customer_status`, где каждая строка — период действия статуса.
|
||||||
|
|
||||||
@@ -216,7 +213,7 @@ ORDER BY customer_bk, valid_from;
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. Часть 3: инкрементальная загрузка (по желанию)
|
## 5. Часть 3 — инкрементальная загрузка (по желанию)
|
||||||
|
|
||||||
Если хочется потренироваться глубже:
|
Если хочется потренироваться глубже:
|
||||||
|
|
||||||
@@ -234,18 +231,31 @@ cat dwh-modeling/data/customer_status_events_increment.csv | ./postgres-bookings
|
|||||||
|
|
||||||
- ориентируйтесь на пример из `03_demo_increment.sql` для `dds.dim_customer`;
|
- ориентируйтесь на пример из `03_demo_increment.sql` для `dds.dim_customer`;
|
||||||
- важно:
|
- важно:
|
||||||
- корректно «закрыть» старую актуальную строку (заполнить `valid_to` датой начала новой версии);
|
- корректно «закрыть» старую актуальную строку (заполнить `valid_to` датой начала новой версии);
|
||||||
- вставить новую строку с `valid_to = NULL`.
|
- вставить новую строку с `valid_to = NULL`.
|
||||||
|
|
||||||
Эта часть особенно полезна, если вы хотите почувствовать, как SCD2 живёт в реальном DWH.
|
Эта часть особенно полезна, если вы хотите почувствовать, как SCD2 живёт в реальном DWH.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Часть 4: витрина в DM (по желанию)
|
## 6. Часть 4 — витрина в DM (по желанию)
|
||||||
|
|
||||||
Опциональное задание для закрепления: собрать небольшую витрину с количеством клиентов по статусам на каждую дату.
|
Опциональное задание для закрепления: собрать небольшую витрину с количеством клиентов по статусам на каждую дату.
|
||||||
|
|
||||||
DDL витрины уже создан в `07_ddl_hw_customer_status.sql` (таблица `dm.mart_customer_status_daily`).
|
Перед началом убедитесь, что слой DM создан (схема `dm` и таблицы):
|
||||||
|
|
||||||
|
- выполните `dwh-modeling/sql/05_ddl_dm.sql` (один раз);
|
||||||
|
- затем можно собирать витрину.
|
||||||
|
|
||||||
|
Пример целевой таблицы:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE dm.mart_customer_status_daily (
|
||||||
|
date_actual DATE NOT NULL,
|
||||||
|
status VARCHAR(20) NOT NULL,
|
||||||
|
customers_cnt INT NOT NULL
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
Идея:
|
Идея:
|
||||||
|
|
||||||
@@ -282,16 +292,3 @@ ORDER BY date_actual, status;
|
|||||||
- при желании — собрать простую витрину в `dm`.
|
- при желании — собрать простую витрину в `dm`.
|
||||||
|
|
||||||
Если что‑то не получается — можно разбирать решения по шагам вместе с ментором: от простого `SELECT` из STG до полноценного SCD2 в DDS.
|
Если что‑то не получается — можно разбирать решения по шагам вместе с ментором: от простого `SELECT` из STG до полноценного SCD2 в DDS.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. Эталонное решение
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Показать ссылку на решение</summary>
|
|
||||||
|
|
||||||
Когда выполните домашку и захотите сверить результат — готовое решение лежит в файле [`09_dml_hw_customer_status_solution.sql`](sql/09_dml_hw_customer_status_solution.sql).
|
|
||||||
|
|
||||||
Постарайтесь не подглядывать до того, как напишете свой вариант — основная ценность задания именно в самостоятельном разборе.
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|||||||
+154
-174
@@ -3,30 +3,28 @@
|
|||||||
|
|
||||||
## Оглавление
|
## Оглавление
|
||||||
|
|
||||||
- [Что вы уже умеете, и что узнаете здесь](#что-вы-уже-умеете-и-что-узнаете-здесь)
|
- [Что вы уже умеете — и что узнаете здесь](#что-вы-уже-умеете-и-что-узнаете-здесь)
|
||||||
- [1. Введение: почему нельзя просто SELECT из базы заказов?](#1-введение-почему-нельзя-просто-select-из-базы-заказов)
|
- [1. Введение: почему нельзя просто SELECT из базы заказов?](#1-введение-почему-нельзя-просто-select-из-базы-заказов)
|
||||||
- [2. Учебный пример: интернет-магазин](#2-учебный-пример-интернет-магазин)
|
- [2. Учебный пример: интернет-магазин](#2-учебный-пример-интернет-магазин)
|
||||||
- [3. Зачем делить DWH на слои?](#3-зачем-делить-dwh-на-слои)
|
- [3. Зачем делить DWH на слои?](#3-зачем-делить-dwh-на-слои)
|
||||||
- [4. Путешествие данных: от STG до DM](#4-путешествие-данных-от-stg-до-dm)
|
- [4. Путешествие данных: от STG до DM](#4-путешествие-данных-от-stg-до-dm)
|
||||||
- [5. Базовые понятия: факты, измерения, SCD](#5-базовые-понятия-факты-измерения-scd)
|
- [5. Базовые понятия: факты, измерения, SCD](#5-базовые-понятия-факты-измерения-scd)
|
||||||
- [6. Модели данных для слоя DDS: 4 подхода, и когда какой выбрать](#6-модели-данных-для-слоя-dds-4-подхода-и-когда-какой-выбрать)
|
- [6. Модели данных для слоя DDS: 4 подхода — и когда какой выбрать](#6-модели-данных-для-слоя-dds-4-подхода-и-когда-какой-выбрать)
|
||||||
- [7. Практикум: как собрать первую витрину](#7-практикум-как-собрать-первую-витрину)
|
- [7. Практикум: как собрать первую витрину](#7-практикум-как-собрать-первую-витрину)
|
||||||
- [8. Как выбрать модель данных? Советы от практиков](#8-как-выбрать-модель-данных-советы-от-практиков)
|
- [8. Как выбрать модель данных? Советы от практиков](#8-как-выбрать-модель-данных-советы-от-практиков)
|
||||||
- [9. Эксплуатация: качество данных, это не «опция»](#9-эксплуатация-качество-данных-это-не-опция)
|
- [9. Эксплуатация: качество данных — это не «опция»](#9-эксплуатация-качество-данных-это-не-опция)
|
||||||
- [10. Заключение: главное, понимать «почему»](#10-заключение-главное-понимать-почему)
|
- [10. Заключение: главное — понимать «почему»](#10-заключение-главное-понимать-почему)
|
||||||
- [Приложения](#приложения)
|
- [Приложения](#приложения)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Что вы уже умеете, и что узнаете здесь
|
## Что вы уже умеете — и что узнаете здесь
|
||||||
|
|
||||||
✅ Уже знаете:
|
✅ Уже знаете:
|
||||||
|
|
||||||
- `SELECT`, `JOIN`, `GROUP BY`;
|
- `SELECT`, `JOIN`, `GROUP BY`;
|
||||||
- как посчитать сумму/среднее/количество по таблице.
|
- как посчитать сумму/среднее/количество по таблице.
|
||||||
|
|
||||||
🆕 Узнаете в этой статье:
|
🆕 Узнаете в этой статье:
|
||||||
|
|
||||||
- **слои хранилища** (STG → ODS → DDS → DM) и *зачем они нужны*;
|
- **слои хранилища** (STG → ODS → DDS → DM) и *зачем они нужны*;
|
||||||
- **факты и измерения** — основные кирпичики аналитики;
|
- **факты и измерения** — основные кирпичики аналитики;
|
||||||
- **SCD Type 2** — как хранить историю изменений клиента (например, смену email или города);
|
- **SCD Type 2** — как хранить историю изменений клиента (например, смену email или города);
|
||||||
@@ -34,7 +32,6 @@
|
|||||||
- **четыре модели данных**: 3NF, Звезда (Star), Data Vault, Anchor Modeling — и когда какую использовать.
|
- **четыре модели данных**: 3NF, Звезда (Star), Data Vault, Anchor Modeling — и когда какую использовать.
|
||||||
|
|
||||||
⛔ **Не будем говорить** здесь о:
|
⛔ **Не будем говорить** здесь о:
|
||||||
|
|
||||||
- физическом хранении (партиции, индексы, ClickHouse-движки);
|
- физическом хранении (партиции, индексы, ClickHouse-движки);
|
||||||
- распределённых кластерах (Kafka, Spark, Airflow — это отдельный курс);
|
- распределённых кластерах (Kafka, Spark, Airflow — это отдельный курс);
|
||||||
- настройке производительности (`EXPLAIN`, кэши и т.п.).
|
- настройке производительности (`EXPLAIN`, кэши и т.п.).
|
||||||
@@ -50,7 +47,6 @@
|
|||||||
Вы идёте в базу заказов — и… не находите email. Он в CRM. Идёте в CRM — там нет сумм заказов. Возвращаетесь в заказы — сумма есть, но *только текущая цена товара*. А в 2023 году цена была другой!
|
Вы идёте в базу заказов — и… не находите email. Он в CRM. Идёте в CRM — там нет сумм заказов. Возвращаетесь в заказы — сумма есть, но *только текущая цена товара*. А в 2023 году цена была другой!
|
||||||
|
|
||||||
Знакомо? Это — **проблема OLTP-систем** (оперативного учёта):
|
Знакомо? Это — **проблема OLTP-систем** (оперативного учёта):
|
||||||
|
|
||||||
- **CRM**, **склад**, **платёжка** — это разные базы;
|
- **CRM**, **склад**, **платёжка** — это разные базы;
|
||||||
- каждая оптимизирована под *быструю запись операций* («добавить заказ», «списать товар»);
|
- каждая оптимизирована под *быструю запись операций* («добавить заказ», «списать товар»);
|
||||||
- историю там не хранят — email меняется «в лоб»: старое значение перезаписывается.
|
- историю там не хранят — email меняется «в лоб»: старое значение перезаписывается.
|
||||||
@@ -79,7 +75,6 @@
|
|||||||
| `promos` | Маркетинг | Акции: `promo_id`, `code` |
|
| `promos` | Маркетинг | Акции: `promo_id`, `code` |
|
||||||
|
|
||||||
⚠️ Обратите внимание:
|
⚠️ Обратите внимание:
|
||||||
|
|
||||||
- `customer_id = 101` в одном месяце — `a@ex.com`, в другом — `b@ex.com`;
|
- `customer_id = 101` в одном месяце — `a@ex.com`, в другом — `b@ex.com`;
|
||||||
- цена на товар `9001` (Phone) в январе — 100 ₽, в феврале — 110 ₽;
|
- цена на товар `9001` (Phone) в январе — 100 ₽, в феврале — 110 ₽;
|
||||||
- `order_items` содержит `price_at_sale` — *цену в момент покупки*, а не текущую.
|
- `order_items` содержит `price_at_sale` — *цену в момент покупки*, а не текущую.
|
||||||
@@ -145,27 +140,27 @@ flowchart TD
|
|||||||
|
|
||||||
Давайте проследим, как превращается строка заказа.
|
Давайте проследим, как превращается строка заказа.
|
||||||
|
|
||||||
### **STG (Staging / Bronze)**: «как пришло»
|
### **STG (Staging / Bronze)** — «как пришло»
|
||||||
|
|
||||||
- Таблицы: `stg.orders_raw`, `stg.customers_raw`;
|
- Таблицы: `stg.orders_raw`, `stg.customers_raw`;
|
||||||
- Структура — *точно как в источнике* (может быть `VARCHAR` даже у дат);
|
- Структура — *точно как в источнике* (может быть `VARCHAR` даже у дат);
|
||||||
- Добавлены технические поля:
|
- Добавлены технические поля:
|
||||||
- `_load_id` — идентификатор загрузки;
|
- `_load_id` — идентификатор загрузки;
|
||||||
- `_load_ts` — время получения данных;
|
- `_load_ts` — время получения данных;
|
||||||
- Главное правило: **неизменяемость**. Если пришла новая порция — либо добавляем новые строки, либо *полностью перезагружаем* слой (идемпотентность).
|
- Главное правило: **неизменяемость**. Если пришла новая порция — либо добавляем новые строки, либо *полностью перезагружаем* слой (идемпотентность).
|
||||||
|
|
||||||
> 💡 *Пример:* `stg.orders_raw` содержит `"2024-01-10"` как строку — это нормально. Главное — не потерять оригинал.
|
> 💡 *Пример:* `stg.orders_raw` содержит `"2024-01-10"` как строку — это нормально. Главное — не потерять оригинал.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### **ODS (Operational Data Store / Silver)**: «почистили, но не трогали смысл»
|
### **ODS (Operational Data Store / Silver)** — «почистили, но не трогали смысл»
|
||||||
|
|
||||||
- Таблицы: `ods.orders`, `ods.customers`;
|
- Таблицы: `ods.orders`, `ods.customers`;
|
||||||
- Здесь:
|
- Здесь:
|
||||||
- привели `order_date` к типу `DATE`;
|
- привели `order_date` к типу `DATE`;
|
||||||
- убрали заказы без клиента (`customer_id IS NULL` → ошибка или флаг);
|
- убрали заказы без клиента (`customer_id IS NULL` → ошибка или флаг);
|
||||||
- привели телефоны к формату `79991112233`;
|
- привели телефоны к формату `79991112233`;
|
||||||
- проверили email на валидность (регуляркой или простой проверкой).
|
- проверили email на валидность (регуляркой или простой проверкой).
|
||||||
- **Но!** Не объединяем клиента из CRM и клиента из заказов — это будет позже.
|
- **Но!** Не объединяем клиента из CRM и клиента из заказов — это будет позже.
|
||||||
- Пока — никакой бизнес-логики. Только *техническая* очистка.
|
- Пока — никакой бизнес-логики. Только *техническая* очистка.
|
||||||
- Дедупликация: если два раза пришёл один и тот же заказ — оставляем один (по `order_id + _load_ts`).
|
- Дедупликация: если два раза пришёл один и тот же заказ — оставляем один (по `order_id + _load_ts`).
|
||||||
@@ -174,7 +169,7 @@ flowchart TD
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### **DDS (Data Delivery Store / Core / Conformed)**: «интеграция + история»
|
### **DDS (Data Delivery Store / Core / Conformed)** — «интеграция + история»
|
||||||
|
|
||||||
Здесь рождается *единая бизнес-модель*.
|
Здесь рождается *единая бизнес-модель*.
|
||||||
Появляются понятия: **измерения**, **факты**, **суррогатные ключи**, **SCD**.
|
Появляются понятия: **измерения**, **факты**, **суррогатные ключи**, **SCD**.
|
||||||
@@ -185,7 +180,7 @@ flowchart TD
|
|||||||
|---------|------------|
|
|---------|------------|
|
||||||
| `dds.dim_customer` | Измерение «Клиент» с историей (SCD Type 2) |
|
| `dds.dim_customer` | Измерение «Клиент» с историей (SCD Type 2) |
|
||||||
| `dds.dim_product` | Измерение «Товар» |
|
| `dds.dim_product` | Измерение «Товар» |
|
||||||
| `dds.dim_date` | Готовый календарь на 5 лет вперёд (день/неделя/месяц/квартал) |
|
| `dds.dim_date` | Готовый календарь на 10 лет вперёд (день/неделя/месяц/квартал) |
|
||||||
| `dds.fact_sales` | Факт «Продажа» — строка заказа с суммой и количеством |
|
| `dds.fact_sales` | Факт «Продажа» — строка заказа с суммой и количеством |
|
||||||
|
|
||||||
💡 **Суррогатный ключ (Surrogate Key, SK)** — это `BIGINT`, который мы генерируем сами (например, `customer_sk = 1001`).
|
💡 **Суррогатный ключ (Surrogate Key, SK)** — это `BIGINT`, который мы генерируем сами (например, `customer_sk = 1001`).
|
||||||
@@ -199,10 +194,9 @@ flowchart TD
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### **DM (Data Mart / Gold / «Витрины»)**: «готово к употреблению»
|
### **DM (Data Mart / Gold/ «Витрины»)** — «готово к употреблению»
|
||||||
|
|
||||||
Здесь — таблицы и представления для конкретных задач:
|
Здесь — таблицы и представления для конкретных задач:
|
||||||
|
|
||||||
- `dm.mart_daily_sales` — ежедневные продажи по товарам и сегментам;
|
- `dm.mart_daily_sales` — ежедневные продажи по товарам и сегментам;
|
||||||
- `dm.mart_customer_360` — полный портрет клиента: сколько потратил, когда заходил, какие товары любит.
|
- `dm.mart_customer_360` — полный портрет клиента: сколько потратил, когда заходил, какие товары любит.
|
||||||
|
|
||||||
@@ -216,13 +210,12 @@ flowchart TD
|
|||||||
> *«10 января 2024 года клиент из Москвы (сегмент Premium) купил Phone за 100 ₽»*.
|
> *«10 января 2024 года клиент из Москвы (сегмент Premium) купил Phone за 100 ₽»*.
|
||||||
|
|
||||||
В DWH это разложится на:
|
В DWH это разложится на:
|
||||||
|
|
||||||
- **Факт (Fact)** — событие, которое можно измерить: *покупка*.
|
- **Факт (Fact)** — событие, которое можно измерить: *покупка*.
|
||||||
Хранится в `fact_sales`: `quantity = 1`, `amount = 100`.
|
Хранится в `fact_sales`: `quantity = 1`, `amount = 100`.
|
||||||
- **Измерения (Dimensions)** — *контекст* факта:
|
- **Измерения (Dimensions)** — *контекст* факта:
|
||||||
- `dim_date` → 10 января 2024;
|
- `dim_date` → 10 января 2024;
|
||||||
- `dim_customer` → Москва, Premium;
|
- `dim_customer` → Москва, Premium;
|
||||||
- `dim_product` → Phone.
|
- `dim_product` → Phone.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
erDiagram
|
erDiagram
|
||||||
@@ -266,10 +259,9 @@ erDiagram
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### SCD Type 2: как хранить историю
|
### SCD Type 2 — как хранить историю
|
||||||
|
|
||||||
Клиент №101:
|
Клиент №101:
|
||||||
|
|
||||||
- с 1 янв по 15 мая — `email = a@ex.com`, `city = Москва`;
|
- с 1 янв по 15 мая — `email = a@ex.com`, `city = Москва`;
|
||||||
- с 16 мая — `email = b@ex.com`, `city = Москва`;
|
- с 16 мая — `email = b@ex.com`, `city = Москва`;
|
||||||
- с 1 окт — `email = b@ex.com`, `city = Санкт-Петербург`.
|
- с 1 окт — `email = b@ex.com`, `city = Санкт-Петербург`.
|
||||||
@@ -289,136 +281,55 @@ AND (dim_customer.valid_to IS NULL OR fact_sales.order_date < dim_customer.valid
|
|||||||
```
|
```
|
||||||
и получаем актуальный на тот день email и город.
|
и получаем актуальный на тот день email и город.
|
||||||
|
|
||||||
> 🔍 Подробнее про SCD — в отдельной статье [Slowly Changing Dimensions](SCD.md) (сравнение Type 1/2/3, паттерны обновления).
|
> 🔍 Подробнее про SCD — в отдельной статье [Slow Changing Dimensions](SCD.md) (сравнение Type 1/2/3, паттерны обновления).
|
||||||
|
|
||||||
Теперь, когда мы разобрались, что такое факты, измерения и SCD, давайте посмотрим, как именно можно устроить слой DDS внутри — есть несколько вариантов.
|
Теперь, когда мы разобрались, что такое факты, измерения и SCD, давайте посмотрим, как именно можно устроить слой DDS внутри — есть несколько вариантов.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Модели данных для слоя DDS: 4 подхода, и когда какой выбрать
|
## 6. Модели данных для слоя DDS: 4 подхода — и когда какой выбрать
|
||||||
|
|
||||||
В DDS мы можем хранить данные по-разному. Это не «правильно/неправильно», а **выбор под задачу**.
|
В DDS мы можем хранить данные по-разному. Это не «правильно/неправильно», а **выбор под задачу**.
|
||||||
|
|
||||||
### 1. 3NF (третья нормальная форма)
|
### 1. 3NF (третья нормальная форма)
|
||||||
*Источник: Билл Инмон (Bill Inmon)*
|
*Источник: Билл Инмон (Bill Inmon)*
|
||||||
|
|
||||||
Если упростить, 3NF - это когда данные о разных бизнес-сущностях хранятся в отдельных таблицах и связываются ключами: клиент, заказ, город, регион, страна и т.д. Вместо одной большой таблицы с большим числом дублирующихся данных мы получаем цепочку таблиц, связанных ключами: `Заказ → Клиент → Город → Регион → Страна`. JOIN-ов становится больше, зато одно и то же свойство (например, название города) хранится в одном месте, а не дублируется в каждой строке заказа.
|
✅ **Плюсы**:
|
||||||
|
- Минимум избыточности при строгих ключах и правилах дедупликации.
|
||||||
|
- Проще поддерживать единую терминологию и НСИ (reference data).
|
||||||
|
- Атрибуты и справочники легко расширять.
|
||||||
|
|
||||||
Исторически подход с ядром в 3NF чаще связывают с Биллом Инмоном (Bill Inmon): сначала проектируют корпоративную модель данных ядра в 3NF (сущности, атрибуты, связи), а уже поверх неё строят витрины.
|
❌ **Минусы**:
|
||||||
|
- Много JOIN даже для простых отчётов.
|
||||||
|
- Историчность (SCD2) усложняет таблицы.
|
||||||
|
- Новые источники дороже гармонизировать (привести к канону).
|
||||||
|
|
||||||

|
📌 **Когда выбирать**:
|
||||||
|
→ Корпоративные DWH, где важна *единая терминология* и *долгосрочная поддержка*.
|
||||||
* Данные о сущностях разнесены по отдельным таблицам: клиент, заказ, продукт.
|
→ Стабильные домены (финансы, НСИ, договоры) и умеренная динамика изменений.
|
||||||
* Минимум дублирования: общие атрибуты хранятся в одном месте, таблицы связаны ключами.
|
|
||||||
|
|
||||||
#### Как выглядел бы наш магазин в 3NF
|
|
||||||
|
|
||||||
В нашем примере `city` лежит прямо в `dim_customer`. В 3NF город стал бы отдельной таблицей, чтобы название хранилось в одном месте:
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
erDiagram
|
|
||||||
dim_city ||--o{ dim_customer : "город"
|
|
||||||
dim_customer ||--o{ fact_sales : "клиент"
|
|
||||||
|
|
||||||
dim_city {
|
|
||||||
int city_id PK
|
|
||||||
varchar city_name "Москва, СПб, ..."
|
|
||||||
}
|
|
||||||
|
|
||||||
dim_customer {
|
|
||||||
bigint customer_sk PK
|
|
||||||
int customer_bk
|
|
||||||
varchar email
|
|
||||||
int city_id FK "ссылка на dim_city"
|
|
||||||
date valid_from
|
|
||||||
date valid_to
|
|
||||||
}
|
|
||||||
|
|
||||||
fact_sales {
|
|
||||||
bigint sale_id PK
|
|
||||||
bigint customer_sk FK
|
|
||||||
int date_key FK
|
|
||||||
int quantity
|
|
||||||
decimal amount
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Теперь, чтобы узнать **«сколько потратил клиент из Москвы за январь 2024?»**, нужно пройти по цепочке:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- 3NF: три JOIN, чтобы добраться до города
|
|
||||||
SELECT SUM(f.amount)
|
|
||||||
FROM dds.fact_sales f
|
|
||||||
JOIN dds.dim_customer c ON f.customer_sk = c.customer_sk
|
|
||||||
JOIN dds.dim_city ct ON c.city_id = ct.city_id
|
|
||||||
JOIN dds.dim_date d ON f.date_key = d.date_key
|
|
||||||
WHERE ct.city_name = 'Москва'
|
|
||||||
AND d.year = 2024 AND d.month = 1;
|
|
||||||
```
|
|
||||||
|
|
||||||
Запрос читаемый, но JOIN-ов уже три - и это для простого вопроса. В реальном ядре цепочка может быть длиннее: `Клиент → Город → Регион → Страна`.
|
|
||||||
|
|
||||||
**Плюсы:**
|
|
||||||
|
|
||||||
* Удобно поддерживать **единую «карту бизнеса»**: где живут «клиент», «заказ», «договор» и как они связаны;
|
|
||||||
* Меньше дублирования: одно и то же свойство хранится в одном месте, проще исправлять ошибки и контролировать качество;
|
|
||||||
* Проще собирать разные витрины поверх ядра: внизу держим детальные данные и связи, наверху показываем «как удобно».
|
|
||||||
|
|
||||||
**Минусы:**
|
|
||||||
|
|
||||||
* Если строить отчёты прямо по ядру, запросы часто получаются тяжёлыми: много `JOIN`-ов и условий;
|
|
||||||
* История (SCD Type 2) увеличивает объём данных и добавляет временной контекст в соединения - запросы становятся сложнее и менее удобными для чтения;
|
|
||||||
* Изменения в бизнес-процессах приходится аккуратно встраивать в существующую модель: чем старше ядро, тем дороже большие переделки.
|
|
||||||
|
|
||||||
Частый паттерн: **ядро в 3NF** (подход ближе к Инмону), витрины - в Звезде.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 2. Звезда (Star Schema)
|
### 2. Звезда (Star Schema)
|
||||||
*Источник: Ральф Кимболл (Ralph Kimball)*
|
*Источник: Ральф Кимболл (Ralph Kimball)*
|
||||||
|
|
||||||
Самый распространённый способ построения таблиц для слоя витрин. Именно эту модель мы использовали в [разделе 5](#5-базовые-понятия-факты-измерения-scd): схема `fact_sales` + `dim_date` / `dim_customer` / `dim_product` - это и есть Звезда.
|
✅ **Плюсы**:
|
||||||
|
- **Простота**: факт + несколько «плоских» измерений;
|
||||||
|
- **Скорость**: BI-системы любят звезду — запросы пишутся за 5 минут;
|
||||||
|
- **Понятно бизнесу**: «продажи по товарам и клиентам» — это ровно то, что в таблицах.
|
||||||
|
|
||||||
Структура:
|
❌ **Минусы**:
|
||||||
|
- Дублирование: город будет повторяться в каждой строке клиента;
|
||||||
* в центре - таблица фактов (события и метрики);
|
- Изменение структуры измерения — дорого (перестроить всю витрину).
|
||||||
* вокруг - измерения, обычно денормализованные («плоские») - широкие таблицы со всеми атрибутами сущности. Мы сознательно избегаем цепочек справочников ради простоты запросов.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Методология Ральфа Кимбалла (Ralph Kimball) как раз делает упор на такие звёздные схемы: витрины, которые максимально просты для чтения и понятны аналитикам и BI-инструментам.
|
|
||||||
|
|
||||||
#### Тот же вопрос - в Звезде
|
|
||||||
|
|
||||||
В Звезде `city` лежит прямо в `dim_customer` (денормализовано). Тот же отчёт выглядит проще:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- Звезда: два JOIN, город - прямо в измерении
|
|
||||||
SELECT SUM(f.amount)
|
|
||||||
FROM dds.fact_sales f
|
|
||||||
JOIN dds.dim_customer c ON f.customer_sk = c.customer_sk
|
|
||||||
JOIN dds.dim_date d ON f.date_key = d.date_key
|
|
||||||
WHERE c.city = 'Москва'
|
|
||||||
AND d.year = 2024 AND d.month = 1;
|
|
||||||
```
|
|
||||||
|
|
||||||
На один JOIN меньше, и не нужно знать, где именно хранится город: он лежит прямо в карточке клиента. Для аналитика или BI-инструмента это большая разница.
|
|
||||||
|
|
||||||
**Плюсы:**
|
|
||||||
|
|
||||||
* проста для понимания: аналитикам и BI-инструментам удобно работать с такой моделью;
|
|
||||||
* меньше `JOIN`-ов - запросы обычно проще и быстрее;
|
|
||||||
* хорошо подходит для витрин под конкретные задачи.
|
|
||||||
|
|
||||||
**Минусы:**
|
|
||||||
|
|
||||||
* измерения денормализованы, поэтому атрибуты дублируются (например, название города повторяется у всех клиентов из этого города);
|
|
||||||
* изменения атрибутов могут требовать обновлять много строк в измерении.
|
|
||||||
|
|
||||||
|
📌 **Когда выбирать**:
|
||||||
|
→ Витрины (DM), а не ядро (DDS);
|
||||||
|
→ Начинающим командам и MVP;
|
||||||
|
→ Когда отчёты — главная цель.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 3. Data Vault 2.0: «конструктор Lego» для больших DWH
|
### 3. Data Vault 2.0 — «конструктор Lego» для больших DWH
|
||||||
|
|
||||||
*Идея: Дэн Линстедт (Dan Linstedt). Цель — так организовать хранилище, чтобы можно было спокойно добавлять новые источники и хранить историю, не ломая старую модель.*
|
*Идея: Дэн Линстедт (Dan Linstedt). Цель — так организовать хранилище, чтобы можно было спокойно добавлять новые источники и хранить историю, не ломая старую модель.*
|
||||||
|
|
||||||
@@ -435,18 +346,88 @@ WHERE c.city = 'Москва'
|
|||||||
- **Satellite (Сателлит)** — *«какие у них свойства и как они менялись»*.
|
- **Satellite (Сателлит)** — *«какие у них свойства и как они менялись»*.
|
||||||
Имя клиента, email, статус заказа, цены — всё с историей изменений.
|
Имя клиента, email, статус заказа, цены — всё с историей изменений.
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
💡 **Главная мысль:**
|
💡 **Главная мысль:**
|
||||||
идентичность, связи и атрибуты живут **в разных таблицах**, поэтому:
|
идентичность, связи и атрибуты живут **в разных таблицах**, поэтому:
|
||||||
|
|
||||||
- историю проще хранить;
|
- историю проще хранить;
|
||||||
- новые источники проще прикручивать;
|
- новые источники проще прикручивать;
|
||||||
- меньше шансов «сломать» старые отчёты.
|
- меньше шансов «сломать» старые отчёты.
|
||||||
|
|
||||||
Data Vault хорошо подходит там, где много разнородных источников, нужна полная история изменений и прозрачный аудит. За гибкость приходится платить сложностью модели и количеством таблиц — поэтому для небольших проектов (2–5 источников, маленькая команда) DV почти наверняка избыточен.
|
---
|
||||||
|
|
||||||
> 🔍 Подробнее про Data Vault — сравнение с 3NF/Звездой, Raw и Business Vault, когда внедрять — в отдельной статье [DataVault: как пережить бурную жизнь источников](DataVault.md).
|
#### Чем DV отличается от 3NF и Звезды
|
||||||
|
|
||||||
|
Если сильно упростить:
|
||||||
|
|
||||||
|
- В **3NF/Звезде** мы часто смешиваем:
|
||||||
|
- бизнес-ключ,
|
||||||
|
- текущие атрибуты,
|
||||||
|
- историю (SCD2)
|
||||||
|
— всё это в одной таблице измерения.
|
||||||
|
|
||||||
|
- В **Data Vault** это *разнесено*:
|
||||||
|
- Hub — только бизнес-ключ;
|
||||||
|
- Satellite — только атрибуты + история;
|
||||||
|
- Link — только связи между сущностями.
|
||||||
|
|
||||||
|
За это приходится платить сложностью модели и количеством таблиц. Зато DV хорошо выдерживает:
|
||||||
|
- много разнородных источников;
|
||||||
|
- «грязные» данные;
|
||||||
|
- жёсткие требования по аудиту и трассировке.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Raw Vault и Business Vault — два слоя
|
||||||
|
|
||||||
|
Часто говорят «Raw Vault» и «Business Vault». Грубо:
|
||||||
|
|
||||||
|
- **Raw Vault** — «как прилетело из источников».
|
||||||
|
Хабы, линкы и сателлиты, максимально близкие к исходным данным.
|
||||||
|
Задача: надёжно собрать и сохранить **полную историю**.
|
||||||
|
|
||||||
|
- **Business Vault** — «как удобно считать дальше».
|
||||||
|
На основе Raw Vault появляются:
|
||||||
|
- служебные таблицы (PIT, Bridge и т.п.),
|
||||||
|
- подготовленные представления под витрины и отчёты,
|
||||||
|
- бизнес-правила (например, что считать «активным клиентом»).
|
||||||
|
|
||||||
|
Дальше поверх этого уже строятся **обычные витрины в формате Звезды**, с которыми работают аналитики.
|
||||||
|
|
||||||
|
Если примерить это к классическим слоям `stg → ods → dds → dm`, то **очень грубо** можно думать так:
|
||||||
|
|
||||||
|
- `stg` всё равно остаётся как «приземление» (landing) из источников;
|
||||||
|
- **Raw Vault** по духу ближе к **ODS**: мало бизнес-логики, зато полная история и интеграция из разных систем;
|
||||||
|
- **Business Vault** ближе к **DDS**: здесь уже живут бизнес-правила и подготовка данных к витринам;
|
||||||
|
- `dm` по-прежнему остаётся витринами в формате Звезды, с которыми работают аналитики и BI.
|
||||||
|
|
||||||
|
Важно: это именно *аналогия для понимания*, а не жёсткое правило проектирования.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Когда DV вам, скорее всего, рано
|
||||||
|
|
||||||
|
Если у вас:
|
||||||
|
|
||||||
|
- 2–5 источников,
|
||||||
|
- небольшая команда (1–2 инженера + аналитик),
|
||||||
|
- задачи уровня «сделать первые отчёты»,
|
||||||
|
|
||||||
|
то **Data Vault почти наверняка избыточен**.
|
||||||
|
Чаще всего хватает связки:
|
||||||
|
|
||||||
|
> `stg → ods → dds (3NF или простая Звезда с SCD2) → dm (Звезда)`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Что важно запомнить из этой статьи
|
||||||
|
|
||||||
|
Для этой статьи достаточно:
|
||||||
|
|
||||||
|
- знать, что **Data Vault** — это способ строить хранилище как **конструктор из Hub/Link/Satellite**,
|
||||||
|
- понимать, что он нужен в первую очередь там, где:
|
||||||
|
- много систем-источников,
|
||||||
|
- нужна *полная* история и прозрачный аудит.
|
||||||
|
|
||||||
|
Детали (Raw vs Business Vault, PIT/Bridge, DV 1.0 vs 2.0 и т.п.) — это уже тема для отдельной, взрослой статьи.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -454,21 +435,27 @@ Data Vault хорошо подходит там, где много разнор
|
|||||||
|
|
||||||
*Источник: Ларс Рёне (Lars Rönnbäck)*
|
*Источник: Ларс Рёне (Lars Rönnbäck)*
|
||||||
|
|
||||||
Anchor Modeling - ещё более атомарный подход к моделированию ядра, чем Data Vault. Если упростить, он «режет» модель на очень мелкие части, чтобы изменения в атрибутах и связях можно было добавлять почти без переделок схемы.
|
Ещё более атомарный подход:
|
||||||
|
- **Anchor** — сущность (клиент, товар);
|
||||||
|
- **Attribute** — атрибут (email, имя);
|
||||||
|
- **Tie** — связь (как Link в DV);
|
||||||
|
- Все таблицы — 2–3 столбца.
|
||||||
|
|
||||||
Основные типы таблиц:
|
✅ **Плюсы**:
|
||||||
|
- **Максимальная гибкость**: поменяли модель — не трогали старые таблицы;
|
||||||
|
- **Бесконечная эволюция**: можно добавлять атрибуты «задним числом».
|
||||||
|
|
||||||
* **Anchor** - сущности (например, «клиент» или «заказ»).
|
❌ **Минусы**:
|
||||||
* **Attribute** - отдельный атрибут сущности, обычно с историей (например, email, город, статус - каждый в своей таблице).
|
- Очень сложные запросы (JOIN’ов — десятки);
|
||||||
* **Tie** - связь между сущностями (например, «клиент ↔ заказ»).
|
- Почти не используется «в чистом виде» — чаще как концепция.
|
||||||
|
|
||||||

|
📌 **Когда выбирать**:
|
||||||
|
→ Экспериментальные проекты;
|
||||||
Плюс подхода - высокая гибкость: проще добавлять новые атрибуты и варианты связей. Минус - цена этой гибкости: получается очень много таблиц, и запросы (и поддержка модели) обычно заметно сложнее, огромное кол-во `JOIN`. Для обычного DWH-проекта это точно не первый выбор - скорее вариант для очень динамичных предметных областей, где структура данных часто меняется.
|
→ Когда схема данных *каждый месяц* радикально меняется.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Сравнение моделей наглядно
|
### Сравнение моделей — наглядно
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
quadrantChart
|
quadrantChart
|
||||||
@@ -540,7 +527,7 @@ flowchart TD
|
|||||||
|
|
||||||
### Готовые SQL-скрипты
|
### Готовые SQL-скрипты
|
||||||
|
|
||||||
Все необходимые скрипты для построения хранилища находятся в папке [`sql/`](https://github.com/dementev-dev/de-roadmap/tree/main/dwh-modeling/sql):
|
Все необходимые скрипты для построения хранилища находятся в папке [`sql/`](sql/):
|
||||||
|
|
||||||
- [`01_ddl_stg-dds.sql`](sql/01_ddl_stg-dds.sql) — создание схем и таблиц (STG, ODS, DDS);
|
- [`01_ddl_stg-dds.sql`](sql/01_ddl_stg-dds.sql) — создание схем и таблиц (STG, ODS, DDS);
|
||||||
- [`02_dml_stg-dds.sql`](sql/02_dml_stg-dds.sql) — первичная загрузка данных и демонстрация SCD2 через полный пересчёт (`full backfill`) из STG;
|
- [`02_dml_stg-dds.sql`](sql/02_dml_stg-dds.sql) — первичная загрузка данных и демонстрация SCD2 через полный пересчёт (`full backfill`) из STG;
|
||||||
@@ -553,17 +540,11 @@ flowchart TD
|
|||||||
|
|
||||||
```sql
|
```sql
|
||||||
-- mart_daily_sales: ежедневные продажи с сегментацией
|
-- mart_daily_sales: ежедневные продажи с сегментацией
|
||||||
-- Полная пересборка (full refresh) - для простоты; в продакшене бывает incremental.
|
CREATE MATERIALIZED VIEW dm.mart_daily_sales AS
|
||||||
TRUNCATE dm.mart_daily_sales;
|
|
||||||
|
|
||||||
INSERT INTO dm.mart_daily_sales (
|
|
||||||
date_actual, product_name, customer_segment, total_qty, total_revenue
|
|
||||||
)
|
|
||||||
SELECT
|
SELECT
|
||||||
d.date_actual,
|
d.date_actual AS order_date,
|
||||||
p.product_name,
|
p.product_name,
|
||||||
-- Сегмент определяем по сумме строки (в реальности может быть атрибутом клиента)
|
c.customer_segment, -- например: 'Premium', 'Basic'
|
||||||
CASE WHEN f.amount >= 200 THEN 'Premium' ELSE 'Basic' END AS customer_segment,
|
|
||||||
SUM(f.quantity) AS total_qty,
|
SUM(f.quantity) AS total_qty,
|
||||||
SUM(f.amount) AS total_revenue
|
SUM(f.amount) AS total_revenue
|
||||||
FROM dds.fact_sales f
|
FROM dds.fact_sales f
|
||||||
@@ -572,14 +553,13 @@ JOIN dds.dim_date d
|
|||||||
JOIN dds.dim_product p
|
JOIN dds.dim_product p
|
||||||
ON f.product_sk = p.product_sk
|
ON f.product_sk = p.product_sk
|
||||||
JOIN dds.dim_customer c
|
JOIN dds.dim_customer c
|
||||||
ON f.customer_sk = c.customer_sk -- факт ссылается на нужную версию SK
|
ON f.customer_sk = c.customer_sk
|
||||||
GROUP BY d.date_actual, p.product_name,
|
AND f.order_date >= c.valid_from
|
||||||
CASE WHEN f.amount >= 200 THEN 'Premium' ELSE 'Basic' END;
|
AND (c.valid_to IS NULL OR f.order_date < c.valid_to) -- SCD!
|
||||||
|
GROUP BY d.date_actual, p.product_name, c.customer_segment;
|
||||||
```
|
```
|
||||||
|
|
||||||
> 💡 В продакшене витрину иногда оформляют как **MATERIALIZED VIEW** - «кэш» результата запроса, который обновляется по расписанию. В нашем примере используем обычную таблицу с `TRUNCATE` + `INSERT` - для учебных целей это нагляднее.
|
> 💡 **Материализованное представление (MATERIALIZED VIEW)** — это «кэш» результата. Обновляется по расписанию (например, ночью).
|
||||||
|
|
||||||
✏️ **Попробуйте сами:** [Домашка: статусы клиента от STG до DDS (и немного DM)](Homework_Customer_Status_DDS_DM.md) — пройдёте тот же путь, но самостоятельно.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -605,7 +585,7 @@ GROUP BY d.date_actual, p.product_name,
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### ✅ Базовые советы: с чего начать, если вы учитесь или делаете первый DWH
|
### ✅ Базовые советы — с чего начать, если вы учитесь или делаете первый DWH
|
||||||
|
|
||||||
1. **Начните с витрины в формате Звезды (Star Schema).**
|
1. **Начните с витрины в формате Звезды (Star Schema).**
|
||||||
— Это просто: одна таблица фактов + несколько «плоских» измерений.
|
— Это просто: одна таблица фактов + несколько «плоских» измерений.
|
||||||
@@ -632,7 +612,7 @@ GROUP BY d.date_actual, p.product_name,
|
|||||||
Почему: ему нужны готовые метрики без сложных JOIN’ов. Звезда даёт понятные таблицы: «продажи по дням и товарам» — без углубления в атомарные сущности.
|
Почему: ему нужны готовые метрики без сложных JOIN’ов. Звезда даёт понятные таблицы: «продажи по дням и товарам» — без углубления в атомарные сущности.
|
||||||
|
|
||||||
— **BI-разработчик в Power BI / Tableau** → **Звезда**
|
— **BI-разработчик в Power BI / Tableau** → **Звезда**
|
||||||
Почему: все инструменты визуализации оптимизированы под star schema. Один факт + несколько измерений = быстрые отчёты и простую модель.
|
Почему: все инструменты визуализации оптимизированы под star schema. Один факт + несколько измерений = быстрые отчёты и простою модель.
|
||||||
|
|
||||||
— **Инженер ML (Data Scientist / ML-инженер)** → **3NF или сырые ODS-таблицы**
|
— **Инженер ML (Data Scientist / ML-инженер)** → **3NF или сырые ODS-таблицы**
|
||||||
Почему: для фичей нужны атомарные события и детальные атрибуты. Машинное обучение ценит полноту и детализацию данных больше, чем удобство отчётов.
|
Почему: для фичей нужны атомарные события и детальные атрибуты. Машинное обучение ценит полноту и детализацию данных больше, чем удобство отчётов.
|
||||||
@@ -660,7 +640,7 @@ GROUP BY d.date_actual, p.product_name,
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 📌 Кратко: что выбрать *сегодня*, если вы только учитесь
|
### 📌 Кратко — что выбрать *сегодня*, если вы только учитесь
|
||||||
|
|
||||||
| У вас… | Делайте… |
|
| У вас… | Делайте… |
|
||||||
|--------|----------|
|
|--------|----------|
|
||||||
@@ -672,7 +652,7 @@ GROUP BY d.date_actual, p.product_name,
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 9. Эксплуатация: качество данных, это не «опция»
|
## 9. Эксплуатация: качество данных — это не «опция»
|
||||||
|
|
||||||
Самая красивая архитектура бессмысленна, если в `mart_daily_sales` — нули.
|
Самая красивая архитектура бессмысленна, если в `mart_daily_sales` — нули.
|
||||||
Поэтому в каждом слое — **контроль качества (DQ, Data Quality)**.
|
Поэтому в каждом слое — **контроль качества (DQ, Data Quality)**.
|
||||||
@@ -715,7 +695,7 @@ SELECT 'OK' WHERE EXISTS (
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 10. Заключение: главное, понимать «почему»
|
## 10. Заключение: главное — понимать «почему»
|
||||||
|
|
||||||
Хранилище данных — это не про «крутые технологии», а про **мышление**:
|
Хранилище данных — это не про «крутые технологии», а про **мышление**:
|
||||||
|
|
||||||
@@ -791,11 +771,11 @@ SELECT 'OK' WHERE EXISTS (
|
|||||||
|
|
||||||
### Мини-датасет (для практики)
|
### Мини-датасет (для практики)
|
||||||
|
|
||||||
Все данные для практики находятся в папке [`data/`](https://github.com/dementev-dev/de-roadmap/tree/main/dwh-modeling/data) — тренируйтесь:
|
Все данные для практики находятся в папке [`data/`](data/) — тренируйтесь:
|
||||||
|
|
||||||
[`customers.csv`](data/customers.csv):
|
[`customers.csv`](data/customers.csv):
|
||||||
```csv
|
```csv
|
||||||
customer_id,email,phone,city,event_ts,_load_id,_load_ts
|
customer_id,email,phone,city,event_ts,_load_id,load_ts
|
||||||
101,a@ex.com,700,Москва,2024-01-01,batch_20240101_0800,2024-01-01 08:00
|
101,a@ex.com,700,Москва,2024-01-01,batch_20240101_0800,2024-01-01 08:00
|
||||||
102,c@ex.com,701,СПб,2024-01-01,batch_20240101_0800,2024-01-01 08:00
|
102,c@ex.com,701,СПб,2024-01-01,batch_20240101_0800,2024-01-01 08:00
|
||||||
101,b@ex.com,700,Москва,2024-05-16,batch_20240516_0800,2024-05-16 08:00
|
101,b@ex.com,700,Москва,2024-05-16,batch_20240516_0800,2024-05-16 08:00
|
||||||
@@ -832,7 +812,7 @@ product_id,valid_from,valid_to,price
|
|||||||
9002,2023-01-01,,50
|
9002,2023-01-01,,50
|
||||||
```
|
```
|
||||||
|
|
||||||
> 📂 Все SQL-скрипты для построения хранилища находятся в папке [`sql/`](https://github.com/dementev-dev/de-roadmap/tree/main/dwh-modeling/sql).
|
> 📂 Все SQL-скрипты для построения хранилища находятся в папке [`sql/`](sql/).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+7
-8
@@ -28,15 +28,15 @@ SCD — это подход к хранению изменений в измер
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. Типы SCD простыми словами
|
## 3. Типы SCD — простыми словами
|
||||||
|
|
||||||
Существует несколько стандартных стратегий обработки изменений. Рассмотрим самые важные.
|
Существует несколько стандартных стратегий обработки изменений. Рассмотрим самые важные.
|
||||||
|
|
||||||
### **Type 0: никогда не меняется**
|
### **Type 0 — Никогда не меняется**
|
||||||
Атрибут фиксирован навсегда. Например, дата рождения клиента.
|
Атрибут фиксирован навсегда. Например, дата рождения клиента.
|
||||||
Такие поля не требуют специальной обработки — они просто не обновляются.
|
Такие поля не требуют специальной обработки — они просто не обновляются.
|
||||||
|
|
||||||
### **Type 1: просто перезаписать**
|
### **Type 1 — Просто перезаписать**
|
||||||
Вы просто делаете `UPDATE`, и старое значение исчезает.
|
Вы просто делаете `UPDATE`, и старое значение исчезает.
|
||||||
|
|
||||||
✅ Просто.
|
✅ Просто.
|
||||||
@@ -44,7 +44,7 @@ SCD — это подход к хранению изменений в измер
|
|||||||
|
|
||||||
> Подходит, если изменение — это исправление ошибки (например, опечатка в имени).
|
> Подходит, если изменение — это исправление ошибки (например, опечатка в имени).
|
||||||
|
|
||||||
### **Type 2: новая строка для новой версии**
|
### **Type 2 — Новая строка для новой версии**
|
||||||
Каждое изменение порождает **новую строку** в таблице. Старая строка остаётся, но помечается как «устаревшая».
|
Каждое изменение порождает **новую строку** в таблице. Старая строка остаётся, но помечается как «устаревшая».
|
||||||
|
|
||||||
✅ Полная история.
|
✅ Полная история.
|
||||||
@@ -53,7 +53,7 @@ SCD — это подход к хранению изменений в измер
|
|||||||
|
|
||||||
> Это **самый распространённый** подход в аналитике.
|
> Это **самый распространённый** подход в аналитике.
|
||||||
|
|
||||||
### **Type 3: добавить колонку «предыдущее значение»**
|
### **Type 3 — Добавить колонку «предыдущее значение»**
|
||||||
В таблице появляются поля вроде `previous_category`, `category_change_date`.
|
В таблице появляются поля вроде `previous_category`, `category_change_date`.
|
||||||
|
|
||||||
✅ Простая история «до/после».
|
✅ Простая история «до/после».
|
||||||
@@ -61,7 +61,7 @@ SCD — это подход к хранению изменений в измер
|
|||||||
|
|
||||||
> Используется редко, чаще как компромисс в очень простых системах.
|
> Используется редко, чаще как компромисс в очень простых системах.
|
||||||
|
|
||||||
### **Type 4, 5, 6: продвинутые гибриды**
|
### **Type 4, 5, 6 — Продвинутые гибриды**
|
||||||
Эти типы существуют, но **встречаются редко** и почти не используются новичками:
|
Эти типы существуют, но **встречаются редко** и почти не используются новичками:
|
||||||
|
|
||||||
- **Type 4**: история выносится в отдельную таблицу («мини-хранилище» для одного измерения).
|
- **Type 4**: история выносится в отдельную таблицу («мини-хранилище» для одного измерения).
|
||||||
@@ -99,7 +99,7 @@ WHERE customer_id = 1;
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Type 2: сохраняем историю (подробнее)
|
### Type 2: сохраняем историю — подробнее
|
||||||
|
|
||||||
Чтобы хранить историю, мы меняем структуру таблицы. Вот ключевые поля:
|
Чтобы хранить историю, мы меняем структуру таблицы. Вот ключевые поля:
|
||||||
|
|
||||||
@@ -283,7 +283,6 @@ LEFT JOIN current_customers c ON n.customer_id = c.customer_id
|
|||||||
|
|
||||||
###### Шаг 3: Вставка новых версий
|
###### Шаг 3: Вставка новых версий
|
||||||
Для подходящих записей создаём новую версию:
|
Для подходящих записей создаём новую версию:
|
||||||
|
|
||||||
- `uuid()` — генерируем уникальный ключ для новой версии
|
- `uuid()` — генерируем уникальный ключ для новой версии
|
||||||
- `current_date` - функция, возвращающая текущую даты
|
- `current_date` - функция, возвращающая текущую даты
|
||||||
- `COALESCE(n.effective_date, current_date)` — устанавливаем дату начала действия новой версии
|
- `COALESCE(n.effective_date, current_date)` — устанавливаем дату начала действия новой версии
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
customer_id,status,event_ts,_load_id,_load_ts
|
customer_id,status,event_ts,_load_id,load_ts
|
||||||
101,new,2024-01-01 09:00:00,batch_20240101_1000,2024-01-01 10:00:00
|
101,new,2024-01-01 09:00:00,batch_20240101_1000,2024-01-01 10:00:00
|
||||||
101,active,2024-02-15 10:30:00,batch_20240215_1100,2024-02-15 11:00:00
|
101,active,2024-02-15 10:30:00,batch_20240215_1100,2024-02-15 11:00:00
|
||||||
101,vip,2024-05-10 11:00:00,batch_20240510_1200,2024-05-10 12:00:00
|
101,vip,2024-05-10 11:00:00,batch_20240510_1200,2024-05-10 12:00:00
|
||||||
|
|||||||
|
@@ -1,4 +1,4 @@
|
|||||||
customer_id,status,event_ts,_load_id,_load_ts
|
customer_id,status,event_ts,_load_id,load_ts
|
||||||
101,active,2024-11-15 09:00:00,batch_20241115_1000,2024-11-15 10:00:00
|
101,active,2024-11-15 09:00:00,batch_20241115_1000,2024-11-15 10:00:00
|
||||||
102,active,2024-05-05 09:30:00,batch_20240505_1000,2024-05-05 10:00:00
|
102,active,2024-05-05 09:30:00,batch_20240505_1000,2024-05-05 10:00:00
|
||||||
103,active,2024-03-20 12:00:00,batch_20240320_1300,2024-03-20 13:00:00
|
103,active,2024-03-20 12:00:00,batch_20240320_1300,2024-03-20 13:00:00
|
||||||
|
|||||||
|
@@ -1,4 +1,4 @@
|
|||||||
customer_id,email,phone,city,event_ts,_load_id,_load_ts
|
customer_id,email,phone,city,event_ts,_load_id,load_ts
|
||||||
101,a@ex.com,700,Москва,2024-01-01,batch_20240101_0800,2024-01-01 08:00
|
101,a@ex.com,700,Москва,2024-01-01,batch_20240101_0800,2024-01-01 08:00
|
||||||
102,c@ex.com,701,СПб,2024-01-01,batch_20240101_0800,2024-01-01 08:00
|
102,c@ex.com,701,СПб,2024-01-01,batch_20240101_0800,2024-01-01 08:00
|
||||||
101,b@ex.com,700,Москва,2024-05-16,batch_20240516_0800,2024-05-16 08:00
|
101,b@ex.com,700,Москва,2024-05-16,batch_20240516_0800,2024-05-16 08:00
|
||||||
|
|||||||
|
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 54 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 54 KiB |
@@ -2,13 +2,6 @@
|
|||||||
-- DML-скрипт: загрузка и трансформация данных
|
-- DML-скрипт: загрузка и трансформация данных
|
||||||
-- Запускается ПОВТОРНО при каждой загрузке (идемпотентно!)
|
-- Запускается ПОВТОРНО при каждой загрузке (идемпотентно!)
|
||||||
-- ===============================================
|
-- ===============================================
|
||||||
--
|
|
||||||
-- Карта загрузки в этом скрипте:
|
|
||||||
-- STG → ODS: orders, order_items, products, customers (снимок: одна строка на BK)
|
|
||||||
-- STG → DDS: dim_customer (SCD2, full backfill напрямую из STG — см. комментарий к п.5)
|
|
||||||
-- ODS → DDS: dim_product, fact_sales
|
|
||||||
-- отдельно: dim_date (генерация календаря)
|
|
||||||
--
|
|
||||||
|
|
||||||
-- 1. STG: имитация загрузки из источников (в реальности — COPY или INSERT из Kafka/NiFi)
|
-- 1. STG: имитация загрузки из источников (в реальности — COPY или INSERT из Kafka/NiFi)
|
||||||
-- ⚠️ В продакшене STG часто очищается перед загрузкой (TRUNCATE), либо используется партицирование по дате
|
-- ⚠️ В продакшене STG часто очищается перед загрузкой (TRUNCATE), либо используется партицирование по дате
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ CREATE TABLE dm.mart_customer_360 (
|
|||||||
customer_bk INT NOT NULL,
|
customer_bk INT NOT NULL,
|
||||||
first_order_date DATE,
|
first_order_date DATE,
|
||||||
last_order_date DATE,
|
last_order_date DATE,
|
||||||
total_line_items INT NOT NULL,
|
total_orders INT NOT NULL,
|
||||||
total_items INT NOT NULL,
|
total_items INT NOT NULL,
|
||||||
lifetime_value NUMERIC(18,2) NOT NULL,
|
lifetime_value NUMERIC(18,2) NOT NULL,
|
||||||
last_email VARCHAR(100),
|
last_email VARCHAR(100),
|
||||||
|
|||||||
@@ -33,14 +33,14 @@ GROUP BY d.date_actual, p.product_name,
|
|||||||
-- Считаем суммы по всей истории его покупок
|
-- Считаем суммы по всей истории его покупок
|
||||||
INSERT INTO dm.mart_customer_360 (
|
INSERT INTO dm.mart_customer_360 (
|
||||||
customer_bk, first_order_date, last_order_date,
|
customer_bk, first_order_date, last_order_date,
|
||||||
total_line_items, total_items, lifetime_value,
|
total_orders, total_items, lifetime_value,
|
||||||
last_email, last_city
|
last_email, last_city
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
c.customer_bk,
|
c.customer_bk,
|
||||||
MIN(d.date_actual) AS first_order_date,
|
MIN(d.date_actual) AS first_order_date,
|
||||||
MAX(d.date_actual) AS last_order_date,
|
MAX(d.date_actual) AS last_order_date,
|
||||||
COUNT(DISTINCT f.sale_id) AS total_line_items, -- строки факта (позиции продаж), не бизнес-заказы
|
COUNT(DISTINCT f.sale_id) AS total_orders, -- считаем строки факта (продажи), не бизнес-заказы
|
||||||
SUM(f.quantity) AS total_items,
|
SUM(f.quantity) AS total_items,
|
||||||
SUM(f.amount) AS lifetime_value,
|
SUM(f.amount) AS lifetime_value,
|
||||||
-- Берём самый свежий email и город клиента
|
-- Берём самый свежий email и город клиента
|
||||||
|
|||||||
@@ -46,11 +46,3 @@ ALTER TABLE dds.dim_customer_status
|
|||||||
CREATE INDEX ix_dim_customer_status_bk_current
|
CREATE INDEX ix_dim_customer_status_bk_current
|
||||||
ON dds.dim_customer_status (customer_bk)
|
ON dds.dim_customer_status (customer_bk)
|
||||||
WHERE valid_to IS NULL;
|
WHERE valid_to IS NULL;
|
||||||
|
|
||||||
-- 4. DM: витрина статусов клиентов по датам (опциональная часть домашки)
|
|
||||||
DROP TABLE IF EXISTS dm.mart_customer_status_daily;
|
|
||||||
CREATE TABLE dm.mart_customer_status_daily (
|
|
||||||
date_actual DATE NOT NULL,
|
|
||||||
status VARCHAR(20) NOT NULL,
|
|
||||||
customers_cnt INT NOT NULL
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -38,6 +38,11 @@
|
|||||||
-- Можно ориентироваться на примеры в 03_demo_increment.sql.
|
-- Можно ориентироваться на примеры в 03_demo_increment.sql.
|
||||||
|
|
||||||
-- 4. DM: витрина статусов клиентов по датам (по желанию)
|
-- 4. DM: витрина статусов клиентов по датам (по желанию)
|
||||||
-- DDL витрины уже создан в 07_ddl_hw_customer_status.sql (dm.mart_customer_status_daily).
|
-- Пример целевой структуры:
|
||||||
|
-- CREATE TABLE dm.mart_customer_status_daily (
|
||||||
|
-- date_actual DATE NOT NULL,
|
||||||
|
-- status VARCHAR(20) NOT NULL,
|
||||||
|
-- customers_cnt INT NOT NULL
|
||||||
|
-- );
|
||||||
-- Идея: на каждую дату взять актуальный статус клиента
|
-- Идея: на каждую дату взять актуальный статус клиента
|
||||||
-- через JOIN dds.dim_customer_status + dds.dim_date.
|
-- через JOIN dds.dim_customer_status + dds.dim_date.
|
||||||
|
|||||||
@@ -2,19 +2,15 @@
|
|||||||
-- 09_dml_hw_customer_status_solution.sql
|
-- 09_dml_hw_customer_status_solution.sql
|
||||||
-- Решение домашки: статусы клиента (STG -> ODS -> DDS SCD2 -> DM)
|
-- Решение домашки: статусы клиента (STG -> ODS -> DDS SCD2 -> DM)
|
||||||
--
|
--
|
||||||
-- Это ЭТАЛОННОЕ РЕШЕНИЕ. Если вы ещё не пробовали решить домашку сами -
|
|
||||||
-- вернитесь к заданию (Homework_Customer_Status_DDS_DM.md) и шаблону (08_dml_hw_customer_status_template.sql).
|
|
||||||
-- Основная ценность задания - в самостоятельном разборе.
|
|
||||||
--
|
|
||||||
-- Что делает этот файл:
|
-- Что делает этот файл:
|
||||||
-- 1) Перекладывает события статусов в ODS (приводит типы, чистит пустое).
|
-- 1) Перекладывает события статусов в ODS (приводит типы, чистит пустое).
|
||||||
-- 2) Строит DDS-измерение со "встроенной историей" (SCD2): периоды valid_from/valid_to.
|
-- 2) Строит DDS-измерение со "встроенной историей" (SCD2): периоды valid_from/valid_to.
|
||||||
-- 3) Загружает инкрементальную порцию событий и обновляет ODS + DDS.
|
-- 3) Показывает пример обновления DDS маленькой порцией (инкремент): закрыть старое + вставить новое.
|
||||||
-- 4) Собирает простую витрину в DM: сколько клиентов в каком статусе по дням.
|
-- 4) Собирает простую витрину в DM: сколько клиентов в каком статусе по дням.
|
||||||
--
|
--
|
||||||
-- Как запускать:
|
-- Как запускать:
|
||||||
-- - для первого знакомства можно запускать файл целиком;
|
-- - для первого знакомства можно запускать файл целиком;
|
||||||
-- - если хотите потренировать инкремент (п.3): добавьте свои события -> запустите блок 3 ещё раз.
|
-- - если хотите потренировать инкремент (п.3): добавьте новые события -> обновите ODS -> запустите блок 3 ещё раз.
|
||||||
--
|
--
|
||||||
-- Важно:
|
-- Важно:
|
||||||
-- - здесь часто используется TRUNCATE (полная очистка), чтобы было легко повторять домашку;
|
-- - здесь часто используется TRUNCATE (полная очистка), чтобы было легко повторять домашку;
|
||||||
@@ -22,10 +18,9 @@
|
|||||||
--
|
--
|
||||||
-- Предусловия (DDL + данные в STG):
|
-- Предусловия (DDL + данные в STG):
|
||||||
-- 1) dwh-modeling/sql/01_ddl_stg-dds.sql
|
-- 1) dwh-modeling/sql/01_ddl_stg-dds.sql
|
||||||
-- 2) dwh-modeling/sql/02_dml_stg-dds.sql (нужен dim_date)
|
-- 2) dwh-modeling/sql/05_ddl_dm.sql
|
||||||
-- 3) dwh-modeling/sql/05_ddl_dm.sql
|
-- 3) dwh-modeling/sql/07_ddl_hw_customer_status.sql
|
||||||
-- 4) dwh-modeling/sql/07_ddl_hw_customer_status.sql
|
-- 4) stg.customer_status_raw заполнена (см. dwh-modeling/Homework_Customer_Status_DDS_DM.md)
|
||||||
-- 5) stg.customer_status_raw заполнена (см. dwh-modeling/Homework_Customer_Status_DDS_DM.md)
|
|
||||||
-- ===============================================
|
-- ===============================================
|
||||||
|
|
||||||
-- ==========================================================
|
-- ==========================================================
|
||||||
@@ -36,13 +31,6 @@
|
|||||||
-- - STG хранит "как пришло" (обычно TEXT);
|
-- - STG хранит "как пришло" (обычно TEXT);
|
||||||
-- - ODS хранит "аккуратно": правильные типы + простая чистка.
|
-- - ODS хранит "аккуратно": правильные типы + простая чистка.
|
||||||
-- Для простоты пересобираем ODS с нуля.
|
-- Для простоты пересобираем ODS с нуля.
|
||||||
--
|
|
||||||
-- Обратите внимание: ods.customer_status хранит ВСЕ события (PK = customer_id + event_ts),
|
|
||||||
-- а не только последнее состояние, как ods.customers (PK = customer_id).
|
|
||||||
-- Причина: источник данных здесь - поток событий ("статус стал X в момент Y"),
|
|
||||||
-- а не снимок ("вот текущие данные клиента"). ODS сохраняет природу источника:
|
|
||||||
-- снимок остаётся снимком, события остаются событиями.
|
|
||||||
-- Благодаря этому full backfill SCD2 (блок 2) строится прямо из ODS, а не из STG.
|
|
||||||
|
|
||||||
TRUNCATE ods.customer_status;
|
TRUNCATE ods.customer_status;
|
||||||
|
|
||||||
@@ -60,10 +48,6 @@ WHERE s.customer_id ~ '^\d+$'
|
|||||||
AND NULLIF(trim(s.event_ts), '') IS NOT NULL
|
AND NULLIF(trim(s.event_ts), '') IS NOT NULL
|
||||||
AND NULLIF(trim(s.status), '') IS NOT NULL;
|
AND NULLIF(trim(s.status), '') IS NOT NULL;
|
||||||
|
|
||||||
-- Проверка: что получилось в ODS
|
|
||||||
SELECT 'ods.customer_status count = ' || COUNT(*) FROM ods.customer_status;
|
|
||||||
SELECT * FROM ods.customer_status ORDER BY customer_id, event_ts;
|
|
||||||
|
|
||||||
-- ==========================================================
|
-- ==========================================================
|
||||||
-- 2) DDS: начальная загрузка SCD2 (full refresh)
|
-- 2) DDS: начальная загрузка SCD2 (full refresh)
|
||||||
-- ==========================================================
|
-- ==========================================================
|
||||||
@@ -132,66 +116,29 @@ SELECT
|
|||||||
FROM framed
|
FROM framed
|
||||||
ORDER BY customer_bk, valid_from;
|
ORDER BY customer_bk, valid_from;
|
||||||
|
|
||||||
-- Проверка: периоды в DDS (у клиента 101 должно быть 4 строки: new -> active -> vip -> churned)
|
|
||||||
SELECT 'dim_customer_status count = ' || COUNT(*) FROM dds.dim_customer_status;
|
|
||||||
SELECT * FROM dds.dim_customer_status ORDER BY customer_bk, valid_from;
|
|
||||||
|
|
||||||
-- ==========================================================
|
-- ==========================================================
|
||||||
-- 3) Инкрементальная загрузка: STG -> ODS -> DDS
|
-- 3) DDS: инкрементальная загрузка SCD2 (по последним событиям)
|
||||||
-- ==========================================================
|
-- ==========================================================
|
||||||
|
|
||||||
-- Имитируем приход новой порции событий (customer_status_events_increment.csv):
|
-- Этот блок нужен, чтобы показать "как это обычно обновляют":
|
||||||
-- - клиент 101: churned -> active (вернулся)
|
-- после новой порции событий мы:
|
||||||
-- - клиент 102: churned -> active
|
|
||||||
-- - клиент 103: new -> active
|
|
||||||
-- - клиент 104: новый клиент, статус new
|
|
||||||
|
|
||||||
-- 3.0) Новые события в STG
|
|
||||||
-- При повторном запуске эти строки добавятся в STG ещё раз (дубли).
|
|
||||||
-- Для демо это не страшно: ODS-вставка ниже использует ON CONFLICT DO NOTHING,
|
|
||||||
-- а SCD2-блок защищён от повторных вставок через NOT EXISTS.
|
|
||||||
-- В продакшене STG обычно очищается перед каждой загрузкой (TRUNCATE / партиция по дате).
|
|
||||||
INSERT INTO stg.customer_status_raw (customer_id, status, event_ts, _load_id, _load_ts) VALUES
|
|
||||||
('101','active','2024-11-15 09:00:00','batch_20241115_1000','2024-11-15 10:00:00'),
|
|
||||||
('102','active','2024-05-05 09:30:00','batch_20240505_1000','2024-05-05 10:00:00'),
|
|
||||||
('103','active','2024-03-20 12:00:00','batch_20240320_1300','2024-03-20 13:00:00'),
|
|
||||||
('104','new', '2024-06-01 08:00:00','batch_20240601_0900','2024-06-01 09:00:00');
|
|
||||||
|
|
||||||
-- 3.1) UPSERT в ODS: добавляем новые события (не трогаем старые)
|
|
||||||
-- PK в ods.customer_status = (customer_id, event_ts), поэтому каждое уникальное
|
|
||||||
-- событие встаёт отдельной строкой. Дубли (одинаковый customer_id + event_ts) игнорируем.
|
|
||||||
INSERT INTO ods.customer_status (
|
|
||||||
customer_id, status, event_ts, _load_id, _load_ts
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
s.customer_id::INT,
|
|
||||||
NULLIF(trim(s.status), ''),
|
|
||||||
NULLIF(trim(s.event_ts), '')::TIMESTAMP,
|
|
||||||
s._load_id,
|
|
||||||
COALESCE(s._load_ts, now())
|
|
||||||
FROM stg.customer_status_raw s
|
|
||||||
WHERE s.customer_id ~ '^\d+$'
|
|
||||||
AND NULLIF(trim(s.event_ts), '') IS NOT NULL
|
|
||||||
AND NULLIF(trim(s.status), '') IS NOT NULL
|
|
||||||
ON CONFLICT (customer_id, event_ts) DO NOTHING;
|
|
||||||
|
|
||||||
-- Проверка: в ODS должны появиться новые строки
|
|
||||||
SELECT 'ods.customer_status after increment = ' || COUNT(*) FROM ods.customer_status;
|
|
||||||
|
|
||||||
-- 3.2) Инкрементальное обновление DDS (SCD2)
|
|
||||||
-- Идея:
|
|
||||||
-- 1) берём по каждому клиенту самое позднее событие из ODS;
|
-- 1) берём по каждому клиенту самое позднее событие из ODS;
|
||||||
-- 2) сравниваем с текущей версией в DDS (valid_to IS NULL);
|
-- 2) сравниваем его с текущей версией в DDS (valid_to IS NULL);
|
||||||
-- 3) если статус изменился - закрываем старую версию и вставляем новую.
|
-- 3) если статус изменился — закрываем старую версию и вставляем новую.
|
||||||
--
|
--
|
||||||
-- Ограничение учебного варианта:
|
-- Ограничение учебного варианта (в домашке можно игнорировать):
|
||||||
-- - если добавили событие "задним числом" со старой датой, этот блок не пересоберёт всю историю.
|
-- - если вы добавили событие "задним числом" со старой датой, этот блок не пересоберёт всю историю.
|
||||||
-- Для такого кейса обычно делают full refresh (блок 2).
|
-- Для такого кейса обычно делают отдельную логику или full refresh.
|
||||||
|
--
|
||||||
|
-- Примечание:
|
||||||
|
-- - в этом файле блок 2 (full refresh) запускается раньше, поэтому сразу после него
|
||||||
|
-- блок 3, скорее всего, ничего не изменит. Зато его можно повторять после новых событий.
|
||||||
|
|
||||||
BEGIN;
|
BEGIN;
|
||||||
-- 3.2a) Закрываем предыдущую актуальную версию
|
-- 3.1) Закрываем предыдущую актуальную версию
|
||||||
WITH ranked AS (
|
WITH ranked AS (
|
||||||
-- ranked: выбираем "самое свежее" событие на клиента
|
-- ranked: выбираем "самое свежее" событие на клиента.
|
||||||
|
-- Если event_ts одинаковый, берём то, что загрузилось позже (_load_ts).
|
||||||
SELECT
|
SELECT
|
||||||
customer_id AS customer_bk,
|
customer_id AS customer_bk,
|
||||||
status,
|
status,
|
||||||
@@ -208,8 +155,8 @@ BEGIN;
|
|||||||
-- delta: ровно одна строка на клиента (самое свежее событие)
|
-- delta: ровно одна строка на клиента (самое свежее событие)
|
||||||
SELECT * FROM ranked WHERE rn = 1
|
SELECT * FROM ranked WHERE rn = 1
|
||||||
),
|
),
|
||||||
current_ver AS (
|
current AS (
|
||||||
-- current_ver: текущие версии в DDS (valid_to IS NULL)
|
-- current: текущие версии в DDS (valid_to IS NULL)
|
||||||
SELECT d.*
|
SELECT d.*
|
||||||
FROM dds.dim_customer_status d
|
FROM dds.dim_customer_status d
|
||||||
WHERE d.valid_to IS NULL
|
WHERE d.valid_to IS NULL
|
||||||
@@ -225,7 +172,7 @@ BEGIN;
|
|||||||
t.eff_date,
|
t.eff_date,
|
||||||
c.customer_status_sk
|
c.customer_status_sk
|
||||||
FROM delta t
|
FROM delta t
|
||||||
JOIN current_ver c
|
JOIN current c
|
||||||
ON c.customer_bk = t.customer_bk
|
ON c.customer_bk = t.customer_bk
|
||||||
WHERE c.hashdiff <> t.hashdiff
|
WHERE c.hashdiff <> t.hashdiff
|
||||||
AND t.eff_date > c.valid_from -- не создаём период нулевой/отрицательной длины
|
AND t.eff_date > c.valid_from -- не создаём период нулевой/отрицательной длины
|
||||||
@@ -233,9 +180,9 @@ BEGIN;
|
|||||||
WHERE d.customer_status_sk = x.customer_status_sk
|
WHERE d.customer_status_sk = x.customer_status_sk
|
||||||
AND d.valid_to IS NULL;
|
AND d.valid_to IS NULL;
|
||||||
|
|
||||||
-- 3.2b) Вставляем новую версию
|
-- 3.2) Вставляем новую версию
|
||||||
WITH ranked AS (
|
WITH ranked AS (
|
||||||
-- ranked/delta/current_ver повторяем отдельно, чтобы блок INSERT читался отдельно от UPDATE
|
-- ranked/delta/current повторяем отдельно, чтобы блок INSERT читался отдельно от UPDATE
|
||||||
SELECT
|
SELECT
|
||||||
customer_id AS customer_bk,
|
customer_id AS customer_bk,
|
||||||
status,
|
status,
|
||||||
@@ -251,14 +198,14 @@ BEGIN;
|
|||||||
delta AS (
|
delta AS (
|
||||||
SELECT * FROM ranked WHERE rn = 1
|
SELECT * FROM ranked WHERE rn = 1
|
||||||
),
|
),
|
||||||
current_ver AS (
|
current AS (
|
||||||
SELECT d.*
|
SELECT d.*
|
||||||
FROM dds.dim_customer_status d
|
FROM dds.dim_customer_status d
|
||||||
WHERE d.valid_to IS NULL
|
WHERE d.valid_to IS NULL
|
||||||
),
|
),
|
||||||
to_insert AS (
|
to_insert AS (
|
||||||
-- to_insert: кого "вставляем":
|
-- to_insert: кого "вставляем":
|
||||||
-- 1) новый клиент (в current_ver нет строки);
|
-- 1) новый клиент (в current нет строки);
|
||||||
-- 2) изменившийся клиент (статус поменялся).
|
-- 2) изменившийся клиент (статус поменялся).
|
||||||
SELECT
|
SELECT
|
||||||
t.customer_bk,
|
t.customer_bk,
|
||||||
@@ -266,7 +213,7 @@ BEGIN;
|
|||||||
t.hashdiff,
|
t.hashdiff,
|
||||||
t.eff_date
|
t.eff_date
|
||||||
FROM delta t
|
FROM delta t
|
||||||
LEFT JOIN current_ver c
|
LEFT JOIN current c
|
||||||
ON c.customer_bk = t.customer_bk
|
ON c.customer_bk = t.customer_bk
|
||||||
WHERE c.customer_status_sk IS NULL
|
WHERE c.customer_status_sk IS NULL
|
||||||
OR (c.hashdiff <> t.hashdiff AND t.eff_date > c.valid_from)
|
OR (c.hashdiff <> t.hashdiff AND t.eff_date > c.valid_from)
|
||||||
@@ -290,11 +237,6 @@ BEGIN;
|
|||||||
);
|
);
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- Проверка: у клиента 101 должна появиться 5-я строка (active с 2024-11-15),
|
|
||||||
-- у 104 - первая строка (new с 2024-06-01)
|
|
||||||
SELECT 'dim_customer_status after increment = ' || COUNT(*) FROM dds.dim_customer_status;
|
|
||||||
SELECT * FROM dds.dim_customer_status ORDER BY customer_bk, valid_from;
|
|
||||||
|
|
||||||
-- ==========================================================
|
-- ==========================================================
|
||||||
-- 4) DM: витрина статусов клиентов по датам (full refresh)
|
-- 4) DM: витрина статусов клиентов по датам (full refresh)
|
||||||
-- ==========================================================
|
-- ==========================================================
|
||||||
@@ -302,18 +244,19 @@ SELECT * FROM dds.dim_customer_status ORDER BY customer_bk, valid_from;
|
|||||||
-- Витрина "снимок на дату":
|
-- Витрина "снимок на дату":
|
||||||
-- для каждого дня считаем, сколько клиентов было в каждом статусе.
|
-- для каждого дня считаем, сколько клиентов было в каждом статусе.
|
||||||
-- Берём календарь dds.dim_date и подбираем статус по периоду valid_from/valid_to.
|
-- Берём календарь dds.dim_date и подбираем статус по периоду valid_from/valid_to.
|
||||||
-- DDL витрины - в 07_ddl_hw_customer_status.sql.
|
|
||||||
|
CREATE TABLE IF NOT EXISTS dm.mart_customer_status_daily (
|
||||||
|
date_actual DATE NOT NULL,
|
||||||
|
status VARCHAR(20) NOT NULL,
|
||||||
|
customers_cnt INT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
TRUNCATE dm.mart_customer_status_daily;
|
TRUNCATE dm.mart_customer_status_daily;
|
||||||
|
|
||||||
WITH bounds AS (
|
WITH bounds AS (
|
||||||
SELECT
|
SELECT
|
||||||
min(valid_from) AS date_from,
|
min(valid_from) AS date_from,
|
||||||
-- CURRENT_DATE для открытых интервалов (valid_to IS NULL = текущий статус),
|
max(coalesce(valid_to, valid_from)) AS date_to
|
||||||
-- иначе витрина не покроет даты после последней смены статуса.
|
|
||||||
-- Нюанс: количество строк в витрине зависит от даты запуска (каждый
|
|
||||||
-- день добавляется ещё один день). Для учебных целей это приемлемо.
|
|
||||||
max(coalesce(valid_to, CURRENT_DATE)) AS date_to
|
|
||||||
FROM dds.dim_customer_status
|
FROM dds.dim_customer_status
|
||||||
)
|
)
|
||||||
INSERT INTO dm.mart_customer_status_daily (
|
INSERT INTO dm.mart_customer_status_daily (
|
||||||
@@ -331,10 +274,3 @@ JOIN dds.dim_customer_status s
|
|||||||
AND (s.valid_to IS NULL OR d.date_actual < s.valid_to)
|
AND (s.valid_to IS NULL OR d.date_actual < s.valid_to)
|
||||||
GROUP BY d.date_actual, s.status
|
GROUP BY d.date_actual, s.status
|
||||||
ORDER BY d.date_actual, s.status;
|
ORDER BY d.date_actual, s.status;
|
||||||
|
|
||||||
-- Проверка: выборочные даты из витрины
|
|
||||||
SELECT 'mart_customer_status_daily count = ' || COUNT(*) FROM dm.mart_customer_status_daily;
|
|
||||||
SELECT *
|
|
||||||
FROM dm.mart_customer_status_daily
|
|
||||||
WHERE date_actual IN ('2024-01-15', '2024-04-10', '2024-09-15', '2024-12-01')
|
|
||||||
ORDER BY date_actual, status;
|
|
||||||
|
|||||||
-29
@@ -1,29 +0,0 @@
|
|||||||
# Конфигурация lychee — CI-проверка внешних ссылок
|
|
||||||
# Используется в .github/workflows/check-links.yml (подхватывается автоматически)
|
|
||||||
# Локальный запуск: docker run --rm -v "$PWD:/input" -w /input lycheeverse/lychee './**/*.md'
|
|
||||||
|
|
||||||
# Служебные каталоги и каталоги, исключённые из сайта
|
|
||||||
exclude_path = ["project", "site"]
|
|
||||||
|
|
||||||
# Локальные адреса стендов (127.0.0.1, localhost и т.п.)
|
|
||||||
exclude_all_private = true
|
|
||||||
|
|
||||||
exclude = [
|
|
||||||
# Режут ботов и датацентровые IP (проверять вручную из браузера)
|
|
||||||
"^https?://habr\\.com",
|
|
||||||
"^https?://stepik\\.org",
|
|
||||||
"^https?://leetcode\\.com",
|
|
||||||
"^https?://realpython\\.com",
|
|
||||||
# YouTube в CI ненадёжен: 429 на пачку запросов, удалённые видео отдают 200
|
|
||||||
"^https?://(www\\.)?youtube\\.com",
|
|
||||||
"^https?://youtu\\.be",
|
|
||||||
# Telegram отдаёт 200 даже для несуществующих каналов
|
|
||||||
"^https?://t\\.me",
|
|
||||||
]
|
|
||||||
|
|
||||||
# 429 (rate limit) не считаем битой ссылкой
|
|
||||||
accept = ["200..=204", "429"]
|
|
||||||
|
|
||||||
max_retries = 2
|
|
||||||
timeout = 30
|
|
||||||
user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0"
|
|
||||||
-88
@@ -1,88 +0,0 @@
|
|||||||
site_name: "DE Roadmap — Data Engineering с нуля до middle"
|
|
||||||
site_url: https://de.dementev.space/
|
|
||||||
site_description: "Роадмап по Data Engineering: SQL, Python, Airflow, Greenplum и далее"
|
|
||||||
site_author: Dmitry Dementev
|
|
||||||
|
|
||||||
repo_url: https://github.com/dementev-dev/de-roadmap
|
|
||||||
repo_name: dementev-dev/de-roadmap
|
|
||||||
|
|
||||||
docs_dir: .
|
|
||||||
site_dir: site
|
|
||||||
|
|
||||||
exclude_docs: |
|
|
||||||
project/
|
|
||||||
postgres-bookings/
|
|
||||||
.github/
|
|
||||||
.claude/
|
|
||||||
site/
|
|
||||||
AGENTS.md
|
|
||||||
CLAUDE.md
|
|
||||||
COMMIT_RULES.md
|
|
||||||
LICENSE
|
|
||||||
.gitignore
|
|
||||||
mkdocs.yml
|
|
||||||
dwh-modeling/TODO.md
|
|
||||||
overrides/
|
|
||||||
|
|
||||||
nav:
|
|
||||||
- Роадмап: README.md
|
|
||||||
- Моделирование данных:
|
|
||||||
- Введение: dwh-modeling/README.md
|
|
||||||
- SCD: dwh-modeling/SCD.md
|
|
||||||
- Data Vault: dwh-modeling/DataVault.md
|
|
||||||
- "Домашка: STG → ODS → DDS → DM": dwh-modeling/Homework_Customer_Status_DDS_DM.md
|
|
||||||
- Разработка с ИИ:
|
|
||||||
- Введение: ai-dev/README.md
|
|
||||||
- Лучшие практики: ai-dev/best-practice.md
|
|
||||||
- Механизм памяти: ai-dev/memory-mechanism.md
|
|
||||||
|
|
||||||
theme:
|
|
||||||
name: material
|
|
||||||
custom_dir: overrides
|
|
||||||
language: ru
|
|
||||||
palette:
|
|
||||||
- scheme: default
|
|
||||||
primary: indigo
|
|
||||||
accent: indigo
|
|
||||||
toggle:
|
|
||||||
icon: material/brightness-7
|
|
||||||
name: Тёмная тема
|
|
||||||
- scheme: slate
|
|
||||||
primary: indigo
|
|
||||||
accent: indigo
|
|
||||||
toggle:
|
|
||||||
icon: material/brightness-4
|
|
||||||
name: Светлая тема
|
|
||||||
features:
|
|
||||||
- navigation.top
|
|
||||||
- navigation.tracking
|
|
||||||
- search.suggest
|
|
||||||
- search.highlight
|
|
||||||
- toc.follow
|
|
||||||
- content.code.copy
|
|
||||||
|
|
||||||
markdown_extensions:
|
|
||||||
- toc:
|
|
||||||
slugify: !!python/object/apply:pymdownx.slugs.slugify
|
|
||||||
kwds:
|
|
||||||
case: lower
|
|
||||||
permalink: true
|
|
||||||
- admonition
|
|
||||||
- pymdownx.details
|
|
||||||
- pymdownx.superfences
|
|
||||||
- pymdownx.highlight
|
|
||||||
- attr_list
|
|
||||||
|
|
||||||
extra:
|
|
||||||
social:
|
|
||||||
- icon: fontawesome/brands/telegram
|
|
||||||
link: https://t.me/dementev_dev
|
|
||||||
name: Написать в Telegram
|
|
||||||
- icon: fontawesome/brands/github
|
|
||||||
link: https://github.com/dementev-dev/de-roadmap
|
|
||||||
name: GitHub
|
|
||||||
|
|
||||||
plugins:
|
|
||||||
- same-dir
|
|
||||||
- search:
|
|
||||||
lang: ru
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block extrahead %}
|
|
||||||
<style>
|
|
||||||
.tg-fab {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 1.6rem;
|
|
||||||
left: 1.6rem;
|
|
||||||
z-index: 100;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 3.2rem;
|
|
||||||
height: 3.2rem;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: #29b6f6;
|
|
||||||
color: #fff;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, .25);
|
|
||||||
transition: transform .2s, box-shadow .2s, background .2s;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
.tg-fab:hover {
|
|
||||||
transform: scale(1.12);
|
|
||||||
box-shadow: 0 4px 16px rgba(0, 0, 0, .3);
|
|
||||||
background: #039be5;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
@media screen and (max-width: 76.25em) {
|
|
||||||
.tg-fab {
|
|
||||||
width: 2.8rem;
|
|
||||||
height: 2.8rem;
|
|
||||||
bottom: 1.2rem;
|
|
||||||
left: 1.2rem;
|
|
||||||
}
|
|
||||||
.tg-fab svg {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<!-- Yandex.Metrika counter -->
|
|
||||||
<script type="text/javascript">
|
|
||||||
(function(m,e,t,r,i,k,a){
|
|
||||||
m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
|
|
||||||
m[i].l=1*new Date();
|
|
||||||
for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }}
|
|
||||||
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)
|
|
||||||
})(window, document,'script','https://mc.yandex.ru/metrika/tag.js?id=108294923', 'ym');
|
|
||||||
|
|
||||||
ym(108294923, 'init', {ssr:true, webvisor:true, clickmap:true, ecommerce:"dataLayer", referrer: document.referrer, url: location.href, accurateTrackBounce:true, trackLinks:true});
|
|
||||||
</script>
|
|
||||||
<noscript><div><img src="https://mc.yandex.ru/watch/108294923" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
|
|
||||||
<!-- /Yandex.Metrika counter -->
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
{{ super() }}
|
|
||||||
<a
|
|
||||||
href="https://t.me/dementev_dev"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener"
|
|
||||||
class="tg-fab"
|
|
||||||
title="Написать в Telegram"
|
|
||||||
aria-label="Написать в Telegram"
|
|
||||||
>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="28" height="28" fill="currentColor">
|
|
||||||
<path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.479.33-.913.492-1.302.48-.428-.012-1.252-.242-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
{% endblock %}
|
|
||||||
-350
@@ -1,350 +0,0 @@
|
|||||||
# ADR: Архитектура сайта de-roadmap
|
|
||||||
|
|
||||||
> Архитектурный документ. Проектные цели и требования — в [PRD](./PRD.md).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Ключевые архитектурные требования
|
|
||||||
|
|
||||||
Из PRD вытекают четыре жёстких ограничения, определяющих выбор инструментов:
|
|
||||||
|
|
||||||
1. **Сайт опционален.** Репозиторий должен полноценно работать без сайта. Удалили конфиг — всё как было.
|
|
||||||
2. **Dual-compatible links.** Внутренние ссылки между `.md` файлами должны работать и на GitHub, и на сайте.
|
|
||||||
3. **Один файл = один источник правды.** Не должно быть копий или генерируемых `.md`. Редактируем один раз — рендерится везде.
|
|
||||||
4. **Zero-effort deploy.** Push в `main` → сайт обновился. Без ручных шагов, без локальной сборки.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Выбор генератора статического сайта
|
|
||||||
|
|
||||||
### Рассмотренные варианты
|
|
||||||
|
|
||||||
| Критерий | MkDocs Material | Docusaurus | Hugo | Astro |
|
|
||||||
|----------|----------------|------------|------|-------|
|
|
||||||
| Язык/экосистема | Python | Node.js (React) | Go | Node.js |
|
|
||||||
| Порог входа | Минимальный — `pip install`, один YAML | Средний — npm, React-компоненты | Средний — Go templates | Высокий — фреймворк |
|
|
||||||
| Поддержка Markdown «как есть» | Отличная, расширения через плагины | Хорошая, но MDX-ориентирован | Хорошая, но shortcodes вместо стандартного MD | Хорошая |
|
|
||||||
| Навигация по длинной странице | TOC sidebar из коробки | Есть, но заточен под многостраничность | Зависит от темы | Зависит от темы |
|
|
||||||
| Поиск | Встроенный (lunr.js), работает offline | Algolia (внешний сервис) или плагин | Нет из коробки | Нет из коробки |
|
|
||||||
| Тёмная тема | Из коробки, переключатель | Из коробки | Зависит от темы | Зависит от темы |
|
|
||||||
| GitHub Pages деплой | Одна команда / готовый Action | Готовый Action | Готовый Action | Готовый Action |
|
|
||||||
| Dual-compatible links | Поддерживает с настройкой `use_directory_urls` | Преобразует ссылки, может ломать GitHub | Преобразует ссылки | Преобразует ссылки |
|
|
||||||
| Один мейнтейнер, Python-стек | ✅ Идеально | ❌ Node.js | ⚠️ Go, но бинарник | ❌ Node.js |
|
|
||||||
|
|
||||||
### Решение: MkDocs Material
|
|
||||||
|
|
||||||
**Почему:**
|
|
||||||
|
|
||||||
- Python-based — совпадает со стеком автора, `pip install` и готово.
|
|
||||||
- Лучшая из коробки поддержка длинных страниц с TOC — именно наш сценарий.
|
|
||||||
- Встроенный поиск без внешних сервисов.
|
|
||||||
- Самый простой конфиг — один `mkdocs.yml`.
|
|
||||||
- Крупнейшее комьюнити среди генераторов документации, активно развивается.
|
|
||||||
- Dual-compatible links решаемы (см. раздел 4).
|
|
||||||
|
|
||||||
**Почему не Docusaurus:** Node.js-зависимость, ориентирован на многостраничные доки с MDX-компонентами — overkill для «красивый рендер Markdown». Преобразование ссылок может конфликтовать с GitHub-форматом.
|
|
||||||
|
|
||||||
**Почему не Hugo:** Быстрый, но Go-шаблоны сложнее отлаживать. Нет встроенного поиска. Для нашего объёма контента скорость сборки не критична.
|
|
||||||
|
|
||||||
**Почему не Astro:** Полноценный веб-фреймворк — избыточен. Подошёл бы, если бы мы строили маркетинговый сайт с интерактивом, но это не текущая цель.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Структура файлов
|
|
||||||
|
|
||||||
### Решение открытого вопроса: `docs/` vs корень репо
|
|
||||||
|
|
||||||
**Решение: `docs_dir: .` (корень репо = корень сайта), с исключениями.**
|
|
||||||
|
|
||||||
Причина: не создаём отдельную папку `docs/`, не дублируем и не перемещаем файлы. MkDocs умеет работать с корнем репо как источником, исключая ненужное.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# mkdocs.yml (в корне репо)
|
|
||||||
docs_dir: .
|
|
||||||
```
|
|
||||||
|
|
||||||
### Решение открытого вопроса: README.md как index
|
|
||||||
|
|
||||||
**Решение: плагин `awesome-pages` или конфигурация `nav` с явным указанием.**
|
|
||||||
|
|
||||||
MkDocs по умолчанию ищет `index.md`. Но мы хотим сохранить `README.md` (GitHub его рендерит на главной репо). Есть два пути:
|
|
||||||
|
|
||||||
- **Вариант A:** Симлинк `docs/index.md → ../README.md`. Но мы не используем `docs/`.
|
|
||||||
- **Вариант B (выбран):** В `mkdocs.yml` явно указать `README.md` как главную:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
nav:
|
|
||||||
- Роадмап: README.md
|
|
||||||
- Моделирование данных:
|
|
||||||
- Введение: dwh-modeling/README.md
|
|
||||||
- SCD: dwh-modeling/SCD.md
|
|
||||||
- Data Vault: dwh-modeling/DataVault.md
|
|
||||||
- "Домашка: STG → DDS → DM": dwh-modeling/Homework_Customer_Status_DDS_DM.md
|
|
||||||
```
|
|
||||||
|
|
||||||
MkDocs Material корректно обрабатывает `README.md` файлы — рендерит их как `index.html` соответствующей директории.
|
|
||||||
|
|
||||||
### Исключения из сборки
|
|
||||||
|
|
||||||
Файлы и папки, которые не должны попасть на сайт:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
exclude_docs: |
|
|
||||||
project/ # проектная документация (PRD, ADR)
|
|
||||||
postgres-bookings/ # скрипты стенда (не контент сайта)
|
|
||||||
AGENTS.md # конфигурация для AI-агентов
|
|
||||||
LICENSE # лицензия (есть в footer)
|
|
||||||
.gitignore
|
|
||||||
```
|
|
||||||
|
|
||||||
### Итоговая структура репо
|
|
||||||
|
|
||||||
```
|
|
||||||
de-roadmap/
|
|
||||||
├── mkdocs.yml ← конфиг сайта (единственный новый файл в корне)
|
|
||||||
├── README.md ← главная страница сайта И главная страница GitHub
|
|
||||||
├── dwh-modeling/
|
|
||||||
│ ├── README.md ← подстраница «Введение в DWH»
|
|
||||||
│ ├── SCD.md ← подстраница
|
|
||||||
│ ├── DataVault.md ← подстраница
|
|
||||||
│ └── Homework_*.md ← подстраница
|
|
||||||
├── postgres-bookings/ ← исключён из сайта
|
|
||||||
├── project/ ← PRD, ADR — исключены из сайта
|
|
||||||
│ ├── PRD.md
|
|
||||||
│ └── ADR.md
|
|
||||||
├── .github/
|
|
||||||
│ └── workflows/
|
|
||||||
│ └── deploy-site.yml ← CI/CD pipeline
|
|
||||||
├── AGENTS.md ← исключён из сайта
|
|
||||||
└── LICENSE
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Стратегия ссылок (Dual Compatibility)
|
|
||||||
|
|
||||||
### Проблема
|
|
||||||
|
|
||||||
GitHub рендерит ссылки вида `[текст](dwh-modeling/SCD.md)` как переход к файлу.
|
|
||||||
MkDocs по умолчанию преобразует `.md` → `.html` и может менять структуру URL.
|
|
||||||
|
|
||||||
### Решение
|
|
||||||
|
|
||||||
Комбинация настроек MkDocs:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
use_directory_urls: true # /dwh-modeling/SCD/ вместо /dwh-modeling/SCD.html
|
|
||||||
```
|
|
||||||
|
|
||||||
И ссылки в Markdown пишем **всегда как относительные пути к `.md` файлам**:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
<!-- Это работает и на GitHub, и в MkDocs -->
|
|
||||||
[Теория про SCD](dwh-modeling/SCD.md)
|
|
||||||
[Введение в DWH](dwh-modeling/README.md)
|
|
||||||
```
|
|
||||||
|
|
||||||
MkDocs Material автоматически резолвит `.md` ссылки в правильные URL сайта.
|
|
||||||
|
|
||||||
### Кириллические якоря
|
|
||||||
|
|
||||||
GitHub генерирует якоря из кириллических заголовков с URL-encoding:
|
|
||||||
`## Базы данных` → `#базы-данных`
|
|
||||||
|
|
||||||
MkDocs Material по умолчанию делает то же самое через расширение `toc`:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
markdown_extensions:
|
|
||||||
- toc:
|
|
||||||
slugify: !!python/object/apply:pymdownx.slugs.slugify
|
|
||||||
kwds:
|
|
||||||
case: lower
|
|
||||||
permalink: true
|
|
||||||
```
|
|
||||||
|
|
||||||
**Риск:** поведение может различаться на edge cases (спецсимволы, эмодзи в заголовках). Митигация — на этапе MVP протестировать все существующие якорные ссылки в README.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. CI/CD Pipeline
|
|
||||||
|
|
||||||
### GitHub Actions Workflow
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# .github/workflows/deploy-site.yml
|
|
||||||
name: Deploy MkDocs to GitHub Pages
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
workflow_dispatch: # ручной запуск для отладки
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pages: write
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: "pages"
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: '3.x'
|
|
||||||
|
|
||||||
- run: pip install mkdocs-material
|
|
||||||
|
|
||||||
- run: mkdocs build --strict
|
|
||||||
# --strict: падает на warnings (битые ссылки, отсутствующие файлы)
|
|
||||||
|
|
||||||
- uses: actions/upload-pages-artifact@v3
|
|
||||||
with:
|
|
||||||
path: site/
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
needs: build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
environment:
|
|
||||||
name: github-pages
|
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
|
||||||
steps:
|
|
||||||
- id: deployment
|
|
||||||
uses: actions/deploy-pages@v4
|
|
||||||
```
|
|
||||||
|
|
||||||
### Что даёт `--strict`
|
|
||||||
|
|
||||||
Сборка упадёт, если:
|
|
||||||
- Есть ссылка на несуществующий `.md` файл.
|
|
||||||
- Есть битый якорь.
|
|
||||||
- Есть warning от MkDocs.
|
|
||||||
|
|
||||||
Это наш автотест ссылок — бесплатно, в CI.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Конфигурация MkDocs Material
|
|
||||||
|
|
||||||
### Минимальный `mkdocs.yml` для MVP
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
site_name: "DE Roadmap — Data Engineering с нуля до middle"
|
|
||||||
site_url: https://dementev-dev.github.io/de-roadmap/
|
|
||||||
site_description: "Роадмап по Data Engineering: SQL, Python, Airflow, Greenplum и далее"
|
|
||||||
site_author: Dmitry Dementev
|
|
||||||
|
|
||||||
repo_url: https://github.com/dementev-dev/de-roadmap
|
|
||||||
repo_name: dementev-dev/de-roadmap
|
|
||||||
|
|
||||||
docs_dir: .
|
|
||||||
site_dir: site
|
|
||||||
|
|
||||||
# Исключаем из сборки
|
|
||||||
exclude_docs: |
|
|
||||||
project/
|
|
||||||
postgres-bookings/
|
|
||||||
AGENTS.md
|
|
||||||
LICENSE
|
|
||||||
.gitignore
|
|
||||||
.github/
|
|
||||||
site/
|
|
||||||
|
|
||||||
nav:
|
|
||||||
- Роадмап: README.md
|
|
||||||
- Моделирование данных:
|
|
||||||
- Введение: dwh-modeling/README.md
|
|
||||||
- SCD: dwh-modeling/SCD.md
|
|
||||||
- Data Vault: dwh-modeling/DataVault.md
|
|
||||||
- "Домашка: STG → DDS → DM": dwh-modeling/Homework_Customer_Status_DDS_DM.md
|
|
||||||
|
|
||||||
theme:
|
|
||||||
name: material
|
|
||||||
language: ru
|
|
||||||
palette:
|
|
||||||
- scheme: default
|
|
||||||
primary: indigo
|
|
||||||
accent: indigo
|
|
||||||
toggle:
|
|
||||||
icon: material/brightness-7
|
|
||||||
name: Тёмная тема
|
|
||||||
- scheme: slate
|
|
||||||
primary: indigo
|
|
||||||
accent: indigo
|
|
||||||
toggle:
|
|
||||||
icon: material/brightness-4
|
|
||||||
name: Светлая тема
|
|
||||||
features:
|
|
||||||
- navigation.top # кнопка «наверх»
|
|
||||||
- navigation.tracking # URL обновляется при скролле
|
|
||||||
- search.suggest # подсказки в поиске
|
|
||||||
- search.highlight # подсветка найденного
|
|
||||||
- toc.follow # TOC следит за скроллом
|
|
||||||
- content.code.copy # кнопка копирования кода
|
|
||||||
|
|
||||||
markdown_extensions:
|
|
||||||
- toc:
|
|
||||||
permalink: true
|
|
||||||
- admonition # сворачиваемые блоки (Спринт 2)
|
|
||||||
- pymdownx.details # для <details>
|
|
||||||
- pymdownx.superfences # вложенные блоки кода
|
|
||||||
- pymdownx.highlight # подсветка синтаксиса
|
|
||||||
- attr_list # атрибуты для элементов
|
|
||||||
|
|
||||||
plugins:
|
|
||||||
- search:
|
|
||||||
lang: ru
|
|
||||||
```
|
|
||||||
|
|
||||||
### Что уже включено в MVP (бесплатно с Material)
|
|
||||||
|
|
||||||
- Тёмная/светлая тема с переключателем.
|
|
||||||
- Полнотекстовый поиск на русском.
|
|
||||||
- TOC (оглавление) в правом sidebar.
|
|
||||||
- Кнопка «наверх» на длинной странице.
|
|
||||||
- URL обновляется при скролле (можно дать ссылку на конкретный раздел).
|
|
||||||
- Подсветка синтаксиса в блоках кода.
|
|
||||||
- Кнопка копирования кода.
|
|
||||||
- Ссылка на GitHub-репозиторий в шапке.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Кастомный домен (Спринт 2)
|
|
||||||
|
|
||||||
Домен `dementev.space` уже есть. Для подключения:
|
|
||||||
|
|
||||||
1. Создать CNAME-запись: `roadmap.dementev.space → dementev-dev.github.io`.
|
|
||||||
2. Добавить файл `CNAME` в корень репо с содержимым `roadmap.dementev.space`.
|
|
||||||
3. В `mkdocs.yml` обновить `site_url`.
|
|
||||||
4. Включить HTTPS в настройках GitHub Pages.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. Риски и митигации
|
|
||||||
|
|
||||||
| Риск | Митигация |
|
|
||||||
|------|-----------|
|
|
||||||
| `docs_dir: .` подхватывает лишние файлы | `exclude_docs` со списком исключений; `--strict` ловит проблемы |
|
|
||||||
| Кириллические якоря ведут себя по-разному | Тестируем все якорные ссылки из README при первом деплое |
|
|
||||||
| `README.md` содержит GitHub-специфичный синтаксис | Проверяем рендер; при необходимости используем MkDocs-совместимые альтернативы |
|
|
||||||
| Зависимость от `mkdocs-material` | Активный проект (30k+ stars), Python-пакет; при необходимости — заморозить версию в `requirements.txt` |
|
|
||||||
| `exclude_docs` — относительно новая фича MkDocs | Альтернатива: `.mkdocsignore` или плагин `mkdocs-exclude`; протестировать на MVP |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. Порядок действий (Спринт 1)
|
|
||||||
|
|
||||||
1. Создать ветку `feature/site`.
|
|
||||||
2. Добавить `mkdocs.yml` в корень репо.
|
|
||||||
3. Добавить `.github/workflows/deploy-site.yml`.
|
|
||||||
4. Добавить `project/PRD.md` и `project/ADR.md`.
|
|
||||||
5. Проверить локально: `pip install mkdocs-material && mkdocs serve`.
|
|
||||||
6. Протестировать все внутренние ссылки и якоря.
|
|
||||||
7. При необходимости — адаптировать ссылки для dual compatibility.
|
|
||||||
8. Merge в `main` → автодеплой → проверить `https://dementev-dev.github.io/de-roadmap/`.
|
|
||||||
9. Включить GitHub Pages (Settings → Pages → Source: GitHub Actions).
|
|
||||||
-169
@@ -1,169 +0,0 @@
|
|||||||
# PRD: Сайт для de-roadmap
|
|
||||||
|
|
||||||
> Проектный документ. Техническая архитектура — в отдельном ADR.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Контекст и мотивация
|
|
||||||
|
|
||||||
### Текущее состояние
|
|
||||||
|
|
||||||
Роадмап по Data Engineering живёт как GitHub-репозиторий ([dementev-dev/de-roadmap](https://github.com/dementev-dev/de-roadmap)):
|
|
||||||
|
|
||||||
- Основной контент — монолитный `README.md` (~700 строк) с полным учебным планом.
|
|
||||||
- Дополнительные материалы — в подпапках (`dwh-modeling/`, `postgres-bookings/`): теория DWH-моделирования, SCD, Data Vault, домашние задания, скрипты.
|
|
||||||
- 109 коммитов, контент активно развивается.
|
|
||||||
- Основной поток менти приходит через маркетплейс ОМ (Осознанная Меркантильность) — платформу менторства с системой отзывов, ранжированием менторов и модерацией. Участие платное (абонентская плата + аукцион за позицию в выдаче).
|
|
||||||
- Роадмап изначально создавался для себя — как конспект подходов к обучению. Со временем стал использоваться и для менти (удобная структура прохождения), и на ознакомительных созвонах с лидами из ОМ (демонстрация программы и подхода к обучению).
|
|
||||||
- Отдельного маркетингового продвижения роадмапа не было, органических приходов «по ссылке из интернета» — ноль. Сайта ментора тоже пока нет — приходить некуда.
|
|
||||||
|
|
||||||
### Проблема
|
|
||||||
|
|
||||||
GitHub README — рабочий, но не презентабельный формат:
|
|
||||||
|
|
||||||
- Нет удобной навигации по длинному документу (только ручной скролл или оглавление-ссылки).
|
|
||||||
- Выглядит как «техническая документация», а не как продукт ментора.
|
|
||||||
- GitHub README плохо индексируется поисковыми системами — роадмап в таком виде даже потенциально не может привлекать органический трафик и «продавать сам себя».
|
|
||||||
- Неудобно давать ссылку потенциальному менти — GitHub-интерфейс отвлекает (issues, commits, файловое дерево).
|
|
||||||
- Нет мобильной адаптации контента (GitHub на телефоне — страдание).
|
|
||||||
|
|
||||||
### Чего НЕ решаем этим проектом
|
|
||||||
|
|
||||||
- **Лидогенерацию и воронку продаж.** Основной поток тёплых лидов идёт через маркетплейс ОМ, и конкурировать с ним по объёму нереалистично — даже топовые менторы не добирают сравнимое количество лидов из других источников. В перспективе сайт может стать элементом маркетинговой воронки (Habr → сайт → менторство), но это отдалённая цель, не влияющая на текущие решения. Сейчас сайт — витрина экспертизы, а не маркетинговый инструмент.
|
|
||||||
- **Масштабирование менторского бизнеса.** Сейчас приоритет — выпустить текущих менти, а не набрать новых.
|
|
||||||
- **Создание LMS / интерактивного курса.** Формат остаётся «структурированный текст + ссылки», без трекинга прогресса и автопроверок.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Цели
|
|
||||||
|
|
||||||
| # | Цель | Метрика успеха |
|
|
||||||
|---|------|----------------|
|
|
||||||
| 1 | Удобная читаемая версия роадмапа | Сайт с навигацией, поиском, мобильной версией |
|
|
||||||
| 2 | Репозиторий остаётся полностью рабочим без сайта | Все `.md` файлы читаемы на GitHub, ссылки между ними работают |
|
|
||||||
| 3 | Минимальные накладные расходы на поддержку | Обновил `.md` → запушил → сайт обновился автоматически |
|
|
||||||
| 4 | Презентабельный вид для демонстрации на созвонах | Ссылка, которую удобно показать лиду из ОМ при обсуждении программы |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Целевая аудитория
|
|
||||||
|
|
||||||
**Основная:** менти (текущие и потенциальные) — джуны и переходящие в DE из смежных областей. Приходят по рекомендации из ОМ или по прямой ссылке от ментора. Им нужно быстро оценить объём программы и найти нужный раздел.
|
|
||||||
|
|
||||||
**Вторичная:** коллеги-инженеры, которые наткнулись на репозиторий через GitHub/поиск. Для них важна полнота контента и техническая достоверность, а не «продающие» элементы.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Требования
|
|
||||||
|
|
||||||
### 4.1. Обязательные (MVP)
|
|
||||||
|
|
||||||
**Контент и структура:**
|
|
||||||
|
|
||||||
- [x] Главная страница сайта = полный текст текущего `README.md` (одна длинная страница, не разбиваем).
|
|
||||||
- [x] Подстраницы из существующих `.md` файлов (`dwh-modeling/README.md`, `SCD.md`, `DataVault.md`, домашки).
|
|
||||||
- [x] Автоматическое оглавление (Table of Contents) по заголовкам H2/H3 на главной странице.
|
|
||||||
- [x] Внутренние ссылки работают и на GitHub, и на сайте (dual-compatible links).
|
|
||||||
|
|
||||||
**Навигация:**
|
|
||||||
|
|
||||||
- [x] Боковая панель с разделами сайта (основной README + подразделы).
|
|
||||||
- [x] Оглавление текущей страницы (правый sidebar / TOC).
|
|
||||||
- [x] Полнотекстовый поиск по сайту.
|
|
||||||
|
|
||||||
**Деплой:**
|
|
||||||
|
|
||||||
- [x] Автоматическая сборка и публикация при пуше в `main`.
|
|
||||||
- [x] Бесплатный хостинг (GitHub Pages).
|
|
||||||
|
|
||||||
**Совместимость с репо:**
|
|
||||||
|
|
||||||
- [x] Все `.md` файлы остаются читаемыми на GitHub «как есть».
|
|
||||||
- [x] `README.md` в корне репо продолжает рендериться на главной странице репозитория.
|
|
||||||
- [x] Добавление сайта не ломает существующую структуру — если удалить конфиг сайта, репо работает как раньше.
|
|
||||||
|
|
||||||
### 4.2. Желательные (Спринт 2+)
|
|
||||||
|
|
||||||
- [x] Кастомный домен: `de.dementev.space` (подключён 2026-03-27).
|
|
||||||
- [x] Тёмная тема (переключатель light/dark).
|
|
||||||
- [x] Сворачиваемые блоки (`<details>`) — точечно, для подсказок/решений в домашках (2026-03-29).
|
|
||||||
- [x] Кнопка «Написать в Telegram» — floating-кнопка + иконки в футере (2026-03-29).
|
|
||||||
- [ ] Иконки/бейджи для статуса разделов (пройден / в процессе / не начат) — пока декоративные, без бэкенда.
|
|
||||||
- [x] Яндекс.Метрика — счётчик 108294923, вебвизор, карта кликов (2026-03-29).
|
|
||||||
|
|
||||||
### 4.3. Явно НЕ делаем
|
|
||||||
|
|
||||||
- Разбивку основного README на отдельные страницы.
|
|
||||||
- Интерактивный трекинг прогресса менти.
|
|
||||||
- Систему авторизации / личный кабинет.
|
|
||||||
- Блог или раздел новостей (для этого есть Telegram-канал).
|
|
||||||
- SEO-оптимизацию и маркетинговые landing pages.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Ограничения
|
|
||||||
|
|
||||||
- **Бюджет: 0 ₽** (кроме домена, если решим подключить кастомный — `dementev.space` уже есть).
|
|
||||||
- **Время на поддержку: минимальное.** Рабочий процесс = «редактирую `.md` → push → сайт обновляется». Не должно быть отдельного шага сборки, ручного деплоя или npm-зависимостей, требующих обновления.
|
|
||||||
- **Стек автора: Python-first.** Решение на Python-тулинге предпочтительнее, чем на Node.js/Ruby (проще отлаживать при необходимости).
|
|
||||||
- **Один мейнтейнер.** Сайт поддерживает один человек — сложность решения должна быть соответствующей.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Итерации
|
|
||||||
|
|
||||||
### Спринт 1 — MVP: «Сайт, который просто работает» ✅ (2026-03-27)
|
|
||||||
|
|
||||||
**Scope:**
|
|
||||||
|
|
||||||
- Конфигурация генератора статического сайта.
|
|
||||||
- CI/CD pipeline (GitHub Actions → GitHub Pages).
|
|
||||||
- Главная страница = README.
|
|
||||||
- Подстраницы из `dwh-modeling/`.
|
|
||||||
- Фикс внутренних ссылок для dual compatibility.
|
|
||||||
- Адаптация Markdown для Python-Markdown (пустые строки перед списками, 4-пробельные отступы, заголовки `#`→`##`).
|
|
||||||
|
|
||||||
**Definition of Done:**
|
|
||||||
|
|
||||||
- ~~Сайт доступен по URL `https://dementev-dev.github.io/de-roadmap/`.~~ → `https://de.dementev.space/`
|
|
||||||
- Все ссылки внутри README работают и на сайте, и на GitHub.
|
|
||||||
- При пуше в `main` сайт автоматически пересобирается за < 2 минут (факт: ~34 сек).
|
|
||||||
- Контент на GitHub выглядит ровно так же, как до добавления сайта.
|
|
||||||
|
|
||||||
### Спринт 2 — «Удобство и навигация» (в процессе)
|
|
||||||
|
|
||||||
**Scope:**
|
|
||||||
|
|
||||||
- ~~Кастомный домен.~~ ✅ `de.dementev.space` (2026-03-27)
|
|
||||||
- ~~Тёмная тема.~~ ✅ из коробки Material (2026-03-27)
|
|
||||||
- ~~Сворачиваемые блоки.~~ ✅ `<details>` точечно в домашках (2026-03-29)
|
|
||||||
- ~~Кнопка «Написать ментору» (Telegram).~~ ✅ floating + футер (2026-03-29)
|
|
||||||
- ~~Базовая аналитика.~~ ✅ Яндекс.Метрика (2026-03-29)
|
|
||||||
|
|
||||||
### Спринт 3 — «Контент и визуал» (по необходимости)
|
|
||||||
|
|
||||||
**Scope:**
|
|
||||||
|
|
||||||
- Визуальная карта роадмапа (интерактивная диаграмма прохождения).
|
|
||||||
- Страница «О менторе» / «Отзывы выпускников» (когда будут выпускники).
|
|
||||||
- Расширение контента: новые разделы роадмапа → автоматически появляются на сайте.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Риски
|
|
||||||
|
|
||||||
| Риск | Вероятность | Влияние | Митигация |
|
|
||||||
|------|-------------|---------|-----------|
|
|
||||||
| Ссылки ломаются при конвертации (GitHub vs сайт) | Средняя | Высокое | Стратегия dual-compatible links в ADR; автотест ссылок в CI |
|
|
||||||
| Кириллические якоря рендерятся по-разному | Средняя | Среднее | Тестирование конкретного генератора; при необходимости — латинские id |
|
|
||||||
| Генератор сайта перестаёт поддерживаться | Низкая | Среднее | Контент в plain Markdown — миграция на другой генератор за день |
|
|
||||||
| Накладные расходы на поддержку растут | Низкая | Среднее | Принцип «сайт опционален»: если мешает — удаляем конфиг, репо работает |
|
|
||||||
| GitHub Pages ограничения (bandwidth, размер) | Очень низкая | Низкое | Для статического сайта с текстом — не актуально |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. Открытые вопросы
|
|
||||||
|
|
||||||
1. **Домен.** Использовать GitHub Pages URL или сразу подключить кастомный домен? Можно решить в Спринте 2.
|
|
||||||
2. **README.md в корне vs docs/.** Оставляем README.md в корне (GitHub его рендерит) и используем его же как `index.md` для сайта, или делаем симлинк / копию? → Решение в ADR.
|
|
||||||
3. **Структура `docs/` директории.** Нужно ли вообще создавать `docs/`, или генератор может работать прямо с корнем репо? → Решение в ADR.
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
# TODO: общие задачи проекта
|
|
||||||
|
|
||||||
Приоритеты: P1 — делаем в первую очередь; P2 — полезно, когда дойдут руки; P3 — идеи под вопросом.
|
|
||||||
|
|
||||||
## P1
|
|
||||||
|
|
||||||
## P2
|
|
||||||
|
|
||||||
- [ ] **Перенос учебника Airflow в de-roadmap.**
|
|
||||||
Перенести 9 глав учебника из `airflow-manual` в `airflow/` (de-roadmap).
|
|
||||||
В `airflow-manual` оставить только стенд (`airflow-docker/`).
|
|
||||||
Добавить навигацию в `mkdocs.yml`, обеспечить dual-compatible links.
|
|
||||||
|
|
||||||
- [ ] **Добавить счётчик Google Analytics** на сайт (MkDocs Material
|
|
||||||
поддерживает GA через `extra.analytics` в `mkdocs.yml`).
|
|
||||||
|
|
||||||
- [ ] **Ориентиры трудозатрат по блокам.**
|
|
||||||
Одна строка на блок («~N часов»), по образцу курса Lakehouse (~12–15 часов).
|
|
||||||
Менти всегда спрашивают «сколько займёт»; ориентир защищает от провала
|
|
||||||
в практику на месяцы.
|
|
||||||
|
|
||||||
- [ ] **Шаблон прогресса менти.**
|
|
||||||
Файл-чеклист по критериям «когда блок считаем пройденным»; менти копирует
|
|
||||||
в свой форк и ведёт коммитами. Живая практика Git с первой недели +
|
|
||||||
прозрачный прогресс для ментора.
|
|
||||||
|
|
||||||
## P3
|
|
||||||
|
|
||||||
- [ ] **Свой dbt-стенд.**
|
|
||||||
Сейчас практика dbt — на чужом jaffle-shop; единственная секция без
|
|
||||||
собственного стенда. Вариант: dbt-модели поверх postgres-bookings или
|
|
||||||
clickstream-стенда.
|
|
||||||
|
|
||||||
- [ ] **Абзац про Data Quality в курсовой.**
|
|
||||||
Валидационный DAG курсовой — это и есть DQ на практике; добавить абзац,
|
|
||||||
как об этом говорить на собеседовании. Новая секция не нужна.
|
|
||||||
|
|
||||||
- [ ] **Иконки/бейджи статуса разделов** (пройден / в процессе / не начат) —
|
|
||||||
декоративные, без бэкенда. Сомнение: статус у каждого менти свой,
|
|
||||||
пересекается с идеей шаблона прогресса — возможно, отпадёт.
|
|
||||||
|
|
||||||
## Сделано
|
|
||||||
|
|
||||||
- [x] **Подраздел «Linux и терминал» в блоке базовых инструментов** —
|
|
||||||
2026-07-12: видео-интро («Девопс на троечку», покрытие проверено по
|
|
||||||
субтитрам) + три статьи (навигация и grep — habr, права — FirstVDS,
|
|
||||||
ssh — Cloud.ru) + опциональный интерактивный курс Hexlet, примечание про
|
|
||||||
WSL для Windows, два новых критерия готовности блока, Linux добавлен
|
|
||||||
в строку оглавления. Отдельной практики нет — ею служат стенды.
|
|
||||||
|
|
||||||
- [x] **CI-проверка внешних ссылок** — 2026-07-12: `lychee.toml` + workflow
|
|
||||||
`check-links.yml` (еженедельно по понедельникам, при битых ссылках создаёт
|
|
||||||
issue). Игнор-лист: habr, stepik, leetcode, realpython (режут ботов),
|
|
||||||
YouTube и t.me (проверка ненадёжна). Попутно исправлена битая ссылка на
|
|
||||||
русские доки Python в README (перевод `/ru/` на docs.python.org умер целиком).
|
|
||||||
|
|
||||||
- [x] **Убрать раздел «Понятие сложности алгоритмов» из README** —
|
|
||||||
2026-07-12, коммит `6f61c0e`: асимптотика перенесена в Python ссылкой
|
|
||||||
на разбор Big O (habr), по SQL — без дублирования, тему покрывает QPT.
|
|
||||||
Reference in New Issue
Block a user