Основи: продукт, код і робоче середовище
Анатомія кодового проєкту: як читати репозиторій і не губитися в коді
У першій главі ми простежили шлях клієнта від кнопки «Забронювати» до серверної програми й збереженого часу. Тепер подивімося на іншу сторону: які файли…
У першій главі ми простежили шлях клієнта від кнопки «Забронювати» до серверної програми й збереженого часу. Тепер подивімося на іншу сторону: які файли складають таку програму, де шукати її правила та як зрозуміти, що саме змінив агент.
Що ви зрозумієте
Після цієї глави ви зможете пояснити, як програма розкладена на диску, відрізнити вихідний код від налаштувань, залежностей і згенерованих файлів, а також прочитати невеликий фрагмент JavaScript і описати його поведінку. Вам не потрібно самостійно писати або запускати код. Глава 3 окремо розгляне виконання команд, а глава 4 — історію змін.
Приклади бронювання нижче вигадані й навчальні. Усі фрагменти коду та конфігурацій — ілюстративні: їх тут не запускали й не перевіряли в конкретному проєкті.
Спершу знайдіть карту проєкту
Репозиторій (repository) — це папка з файлами проєкту та даними, потрібними системі контролю версій, наприклад Git, щоб зберігати зміни. У командній роботі репозиторій часто містить код, документацію, перевірки й налаштування, але немає правила, що кожен проєкт має містити той самий набір папок.
Каталог — інша назва папки. Шлях (path) показує, як дійти до файла всередині папок: наприклад, src/booking.js. Розширення — остання частина назви після крапки, наприклад .js або .md. Це корисна підказка про формат, але сама назва не гарантує, що вміст справді відповідає цьому формату. Розширення .md зазвичай позначає Markdown — формат тексту з простими позначками.
README (буквально англійською «прочитай мене») — файл із назвою на кшталт README.md, який зазвичай пояснює, для чого проєкт, як ним користуватися, де шукати допомогу та що потрібно знати перед змінами. Це добра перша зупинка, а не повна й гарантовано актуальна специфікація. Документація GitHub описує README як місце для призначення проєкту, користі, початку роботи й контактів підтримки (GitHub Docs).
Модуль (module) — частина програми з власними іменами й межами видимості, яка може відкривати вибрані функції чи значення для інших частин. У JavaScript модулі часто відповідають окремим файлам із export та import; в інших мовах межа модуля може мати іншу форму.
Точка входу (entry point) — файл або команда, з якої конкретне середовище починає завантажувати програму. Вона може бути в скрипті запуску або налаштуваннях інструмента; не кожен проєкт починається з index.js. У Node.js поля main та exports у маніфесті описують точки входу пакета для імпорту, а не обов'язково старт усього застосунку (Node.js: Modules: Packages).
У дереві нижче є JSON (JavaScript Object Notation — нотація об'єктів JavaScript), текстовий формат структурованих даних, і YAML (YAML Ain't Markup Language — «YAML — це не мова розмітки», рекурсивна назва), який часто використовують для конфігурацій (ECMA-404, YAML 1.2.2). HTML (HyperText Markup Language — мова розмітки гіпертексту) задає структуру вебсторінки, а CSS (Cascading Style Sheets — каскадні таблиці стилів) — її оформлення (MDN (Mozilla Developer Network, нині MDN Web Docs): Creating the content, MDN: What is CSS?).
Нижче — можливе, але не універсальне дерево маленького сервісу запису:
booking-demo/
├── README.md
├── package.json
├── package-lock.json
├── .gitignore
├── .env.example
├── src/
│ ├── app.js
│ ├── booking.js
│ └── booking-form.js
├── docs/
│ └── booking-rules.md
├── tests/
│ ├── booking.test.js
│ └── samples/
│ └── busy-times.json
├── scripts/
│ └── build.js
├── config/
│ └── local.yaml
├── migrations/
│ └── 001-create-bookings.migration
├── public/
│ ├── index.html
│ └── styles.css
├── assets/
│ └── logo.txt
├── dist/
│ └── app.bundle.js
└── .cache/
Кожен рядок показує лише можливу роль:
srcскорочує слово source — джерело. Тут може лежати код, який команда підтримує вручну.docsмістить пояснення для людей: правила запису, рішення, інструкції чи обмеження.testsмістить перевірки очікуваної поведінки;samplesу прикладі — тестові дані, а не код продукту.scriptsмістить допоміжні програми для повторюваних дій, наприклад підготовки файлів. Саме назва файла не доводить, що команда є безпечною: скрипт може виконувати довільні дії.configмістить налаштування. Частина налаштувань може бути поруч із кодом, частина — надходити з оточення запуску.migrationsчасто зберігає кроки переходу між версіями структури даних. Їх порядок і повторне виконання можуть мати наслідки, тому це не звичайні чернетки.publicу вебпроєкті може містити матеріали, що браузер може отримати напряму. У прикладіindex.htmlвикористовує HTML для структури сторінки, аstyles.css— CSS для її вигляду. Інші проєкти можуть мати інші каталоги для клієнтського коду.assets— статичні матеріали, наприклад шрифти, іконки чи зображення. Навчальне дерево використовує умовний текстовий файл, а не справжнє зображення.distзазвичай скорочує distribution — підготовлений для поширення результат.app.bundle.jsу прикладі створюється скриптом збірки, а не є першоджерелом для редагування..cache— кеш, тимчасові дані, які інструмент може створити повторно. Його вміст не завжди можна видалити без наслідків; спершу з'ясуйте, хто його створює і для чого.
Файли, що починаються з крапки, часто приховані у звичайному перегляді папок. .gitignore описує, які ще не відстежувані файли Git має ігнорувати. Він не є універсальним засобом видалення секретів: якщо файл уже доданий до репозиторію, сам запис у .gitignore не перестає відстежувати його (GitHub Docs: Ignoring files). .env.example може показувати назви очікуваних налаштувань із безпечними прикладами, але він теж не підтверджує, як саме застосунок завантажує їх.
Структура показує, де що може лежати, але не доводить архітектуру. Наприклад, папка services може містити одну програму або багато; назва backend не пояснює межі компонентів. Правила одного фреймворку — готового набору інструментів і домовленостей — не стають правилом для всіх мов і команд. Спершу перевірте README, файли налаштувань, сценарії збірки та те, звідки код імпортується.
Дані, поведінка і маленькі конструкції коду
Значення — конкретні дані, з якими працює програма. У JavaScript рядок "11:30" є текстом, число 690 є числом, а true і false — булевими значеннями, тобто відповідями «так» або «ні». Рядок із цифрами все одно залишається текстом, доки його явно не перетворили на число.
Змінна — назване місце, через яке код звертається до значення. У JavaScript змінну оголошують, наприклад, через const або let. const забороняє перепризначити саме змінну; якщо вона посилається на об'єкт, це не обов'язково забороняє змінювати властивості того об'єкта.
Тип описує, які значення має змінна і які операції мають для них сенс. У JavaScript тип належить значенню: одна й та сама змінна може в різних рядках отримати число, а потім текст. Це відрізняється від мов, де статична перевірка типів може попереджати про частину невідповідностей ще до виконання. MDN розглядає базові типи, змінні, область видимості й правила JavaScript в посібнику з граматики та типів.
Масив (array) — упорядкований список значень. Об'єкт (object) — набір значень, до яких звертаються за іменованими властивостями. Цей фрагмент пояснений по рядках:
const busyStarts = [660, 720];
const booking = {
guestName: "Олена",
startMinute: 690,
};
const displayName = booking.guestName;
busyStarts— масив чисел, тут хвилини від початку дня:660означає 11:00,720— 12:00.booking— об'єкт із двома названими властивостями. Його полеguestNameмістить рядок, аstartMinute— число.displayNameдістає значення властивостіguestName, тобто рядок"Олена".
Приклад не перевіряє, чи бронювання справжнє або доступне: він лише показує форму даних. У JavaScript пропущена властивість об'єкта зазвичай дає значення undefined; null — окреме явне значення «тут нічого немає». Для розуміння коду важливо з'ясувати, що саме означає відсутність: незаповнене поле, невідомий результат пошуку чи помилку даних. Звернення до властивості null може завершитися помилкою типу під час виконання, тому інтерфейсні дані зазвичай перевіряють перед використанням.
Функція — названий фрагмент коду, який можна викликати. Вона може отримати параметри — вхідні значення — і повернути результат через return. Область видимості (scope) визначає, звідки доступні оголошені змінні. Локальна змінна всередині функції не стає автоматично доступною в усій програмі. Умовний оператор if обирає, які рядки виконати за умовою; цикл, наприклад for...of, повторює дію для елементів списку. MDN описує функції, умови й цикли у посібнику з функцій та посібнику з керування потоком.
Приклад коду, який перевіряє просту доступність:
function canBook(startMinute, busyStarts) {
if (typeof startMinute !== "number" || !Number.isFinite(startMinute)) {
return false;
}
for (const busyStart of busyStarts) {
if (startMinute === busyStart) {
return false;
}
}
return true;
}
Функція отримує число startMinute та масив busyStarts. Вона перевіряє startMinute і відхиляє його, якщо це не скінченне число: сюди належать null, undefined і текстове значення на кшталт "690". Масив busyStarts код не перевіряє й припускає, що він містить числа. Інакше цикл порівнює вибраний час із кожним зайнятим. При збігу повертається false; якщо цикл завершився без збігу, повертається true. Наприклад, canBook(690, [660, 720]) дає true, canBook(720, [660, 720]) — false, а canBook(undefined, [660, 720]) — false.
Це фрагмент лише про точний збіг хвилин. Він не враховує тривалість зустрічі, робочі дні, часовий пояс, права користувача чи зміну розкладу між перевіркою і збереженням. true тут означає тільки «ця умова пропускає значення», а не «бронювання гарантовано збережено».
Приклад 1. Від кнопки до збереження бронювання
Ситуація. Уявний квиток підтримки каже: «Кнопка показує форму, але після натискання запис не з'являється».
Розбір дерева. У цьому умовному проєкті шлях може бути таким:
public/index.htmlоголошує кнопку й форму, які бачить відвідувач;src/app.jsможе з'єднати частини програми.src/booking-form.jsреагує на подію натискання, читає вибраний час і передає його далі.src/booking.jsмістить правила та функцію збереження. Серверна частина або інший адаптер може передати запит до сховища; конкретне місце цього коду залежить від проєкту.tests/booking.test.jsмістить перевірки й описує очікування: доступний час приймається, зайнятий — відхиляється;tests/samples/busy-times.json— зразок даних для них, а не логіка програми.docs/booking-rules.mdпояснює правила людям, аREADME.mdдопомагає знайти загальну інформацію.scripts/build.jsможе читати джерельні файли й створюватиdist/app.bundle.js. У такому разіsrcє джерелом, аdist— виходом збірки.config/local.yamlпоказує можливе локальне налаштування; файл міграції описує зміну структури сховища. Це назви й ролі саме в навчальному прикладі, їх не можна переносити на чужий репозиторій без перевірки. Міграції виконують у визначеному порядку й можуть змінювати дані, тому їх не слід сприймати як звичайні чернетки.
Малий фрагмент зв'язку файлів, лише для пояснення:
// src/booking.js
export function canBook(startMinute, busyStarts) {
return !busyStarts.includes(startMinute);
}
export function saveBooking(startMinute, store) {
return store.save({ startMinute });
}
// src/booking-form.js
import { canBook, saveBooking } from "./booking.js";
function onSubmit(startMinute, busyStarts, store) {
if (!canBook(startMinute, busyStarts)) {
return false;
}
return saveBooking(startMinute, store);
}
export позначає, що модуль відкриває функцію для використання в іншому файлі. import бере її з указаного шляху. У canBook метод includes питає, чи є точне значення в масиві, а знак ! змінює відповідь на протилежну: доступним вважається час, якого ще немає у списку зайнятих. Форма спершу питає правило canBook; якщо воно повернуло false, onSubmit завершується без збереження. Інакше вона викликає saveBooking, а та передає об'єкт бронювання абстрактному store. Тут store.save — умовний контракт сховища, а не конкретний сервер чи база даних. Фрагмент іще не доводить, що кнопка під'єднана до onSubmit: щоб встановити реальний шлях події, треба читати код навколо неї та її виклики.
Результат розбору. Людина, яка аналізує квиток, не мусить знати всі рядки програми, щоб побачити можливу межу проблеми: візуальна форма, обробник натискання, правило доступності, збереження та перевірки — різні ролі. Це підказує, які файли варто дослідити й зіставити з вимогою.
Альтернатива й межа. У проєкті на серверних шаблонах або в мобільному застосунку шлях від кнопки може бути зовсім іншим. Дерево тут — умовна карта для одного прикладу, не готовий шаблон архітектури. Папка з назвою tests також не гарантує, що потрібний сценарій уже перевіряється.
Файл даних не те саме, що програмна логіка
Формат даних задає правила запису й читання інформації. Поведінка — правила, за якими програма приймає рішення або виконує дію. Налаштування може змінювати поведінку, хоча саме по собі не є кодом програми.
JSON — поширений текстовий формат для структурованих даних. Його стандарт не допускає коментарів або зайвої кінцевої коми:
{
"port": 4100,
"mode": "preview"
}
Втрата коми між властивостями може зробити файл непридатним для читання парсером — програмою, що розбирає формат. У результаті застосунок може не стартувати ще до виконання бізнес-правила бронювання. Це синтаксична проблема формату, а не зміна правила, хто має право забронювати час. Правила JSON задає окрема специфікація Ecma International (ECMA-404).
YAML часто використовують для конфігурацій. Відступи в ньому мають значення: вони показують вкладеність.
booking:
reminderMinutes: 30
allowGuest: false
Тут reminderMinutes та allowGuest є полями всередині booking. Якщо випадково змінити відступ, інструмент може прочитати іншу структуру або повідомити про помилку. Поведінка залежить також від того, як конкретний застосунок використовує прочитане значення. Правила мови описані в YAML 1.2.2.
Markdown — формат тексту з простими позначками для заголовків, списків, посилань і блоків коду. Він допомагає структурувати текст для читача, але звичайний Markdown не виконує правило «забронювати, якщо час вільний». Різні сайти підтримують розширення до спільної специфікації, тож відображення може дещо різнитися; базові приклади формату описує CommonMark 0.31.2.
Коментар також залежить від формату. Коментар у JavaScript, позначений //, підказує читачеві, але не є командою програми. YAML має власні коментарі з #. Стандартний JSON не має синтаксису коментарів. Markdown може показати рядок як звичайний текст. Не копіюйте позначку коментаря з одного файла в інший, не перевіривши формат.
Конфігурація й змінні середовища
Конфігурація — значення, які визначають, як саме запустити програму в певному оточенні: наприклад номер локального порту, мову інтерфейсу або адресу тестового сервісу. Змінна середовища — назване значення, яке середовище запуску передає програмі окремо від її вихідного коду.
.env — поширена назва текстового файла з такими парами НАЗВА=значення. Але однаковий формат файла не гарантує однакової поведінки: різні мови та інструменти читають його за власними правилами. Документація Node.js прямо зазначає, що для .env немає єдиної формальної специфікації для всіх платформ, і визначає правила свого середовища (Node.js: Environment Variables).
Налаштування не обов'язково є секретом: APP_PORT=4100 може бути звичайною конфігурацією. API (Application Programming Interface — програмний інтерфейс взаємодії) — узгоджений спосіб, яким одна програма просить іншу надати дані або виконати дію. Ключ для зовнішнього API, наприклад PAYMENT_API_KEY, може бути секретом. Такий рядок нижче — лише безпечний заповнювач, не справжній ключ:
APP_PORT=4100
PAYMENT_API_KEY=replace-with-local-secret
Приклад .gitignore може містити .env, а репозиторій може зберігати .env.example з назвами полів і вигаданими значеннями. Це знижує ризик випадкового додавання локального файла, але не робить файл захищеним і не відкликає секрет, який уже потрапив до історії репозиторію. Секрети слід передавати лише узгодженим захищеним способом; не копіюйте справжні ключі в код, приклади чи журнали.
Пакети, версії й lockfile
Пакет — одиниця коду, яку можна описати та поширювати в певній екосистемі. Менеджер пакетів — інструмент, що допомагає знаходити пакети, добирати сумісні версії та встановлювати їх. У прикладах нижче це npm — назва менеджера пакетів екосистеми Node.js. Інші мови й інструменти мають власні маніфести та lockfile; назви файлів і правила не є спільним стандартом для всіх.
Маніфест (manifest) — файл із метаданими й прямими вимогами проєкту. Для npm це зазвичай package.json, файл у форматі JSON. Пряма залежність — пакет, який проєкт оголосив для власної роботи. Транзитивна залежність — пакет, від якого залежить одна з прямих залежностей. Менеджер пакетів узгоджує ці зв'язки разом.
Це не весь маніфест, а уривок із package.json відкритої бібліотеки p-limit версії 7.3.3, зафіксованої тегом. Значення взяті з її маніфесту:
{
"name": "p-limit",
"version": "7.3.3",
"type": "module",
"scripts": {
"test": "xo && ava && tsd"
},
"dependencies": {
"yocto-queue": "^1.2.1"
},
"devDependencies": {
"ava": "^6.4.1",
"in-range": "^3.0.0",
"random-int": "^3.0.0",
"time-span": "^5.1.0",
"tsd": "^0.33.0",
"xo": "^1.2.1"
}
}
name і version називають пакет і його версію. Поле type повідомляє Node.js трактувати файли .js як модулі формату ECMAScript. scripts задає іменовані команди; тестовий сценарій у зафіксованому маніфесті послідовно запускає xo, ava і tsd, а оператор && запускає наступну команду лише після успішного завершення попередньої. Такі скрипти можуть виконувати код, тому перед запуском важливо перевірити їх призначення та походження. dependencies містить пряму залежність yocto-queue, а devDependencies — пакети, потрібні розробникам для перевірок і супутньої роботи. Поділ залежить від того, що потрібне користувачеві пакета, а що — лише під час розробки, перевірки чи збирання. npm пояснює різницю в документації залежностей.
Запис версії на кшталт ^1.2.1 є діапазоном вимог npm, а не твердженням, що всі майбутні версії однаково сумісні з конкретним застосунком. Версія залежності може бути несумісною з іншою бібліотекою, середовищем запуску або очікуваннями проєкту. Перевірте правила вибраного менеджера пакетів, вимоги до середовища та актуальні тести; не переносіть значення ^ або схеми версій на інші екосистеми без перевірки.
package-lock.json фіксує дерево залежностей, яке npm обрав для цього проєкту, і показує його зміни як текстовий diff. Це службовий результат, який зазвичай створює й оновлює менеджер пакетів, а не список, який варто довільно редагувати вручну. Lockfile підвищує повторюваність встановлення, але не гарантує тотожної роботи в різних версіях npm, операційних системах, сервісах чи змінних середовища (npm package-lock).
Типи перевірок не замінюють одна одну
Перед зміною коду корисно знати, яке питання ставить кожен етап:
| Етап | Що може виявити | Чого результат сам по собі не доводить |
|---|---|---|
| Розбір синтаксису | Неправильні дужки, лапки, коми або ключові слова | Що бізнес-правило відповідає потребі клієнта |
| Статична перевірка типів | Деякі невідповідності між оголошеними типами чи використаннями | Що зовнішні дані мають очікувану форму або що логіка правильна |
| Збірка (build) | Деякі помилки перетворення файлів, імпортів чи конфігурації збірки | Що весь застосунок правильно працює для реальних сценаріїв |
| Запуск | Помилки, що виникають у середовищі виконання або через відсутні налаштування | Що невипробувані сценарії теж працюють |
| Тест поведінки | Відповідь на конкретне перевірене очікування | Що перевірено кожну комбінацію входів і всі зовнішні залежності |
Статична перевірка типів аналізує значення й їх використання до того, як відповідний сценарій запустився. Наприклад, TypeScript — мова й інструмент для перевірки програм на JavaScript — може повідомити, що число передано туди, де оголошено текст. Але дані, отримані з форми або мережі, все одно можуть бути неправильними: типи не перевіряють усі фактичні значення автоматично. Документація TypeScript описує його як статичний перевіряльник; типові позначки стираються під час перетворення й не стають автоматичним захистом під час виконання (TypeScript Handbook: TypeScript for the New Programmer).
Інтерпретація й компіляція — способи, якими інструменти обробляють код, але сучасне середовище може поєднувати їх. Компіляція зазвичай перетворює код перед виконанням або під час нього; збірка може додатково об'єднати файли чи згенерувати нові. Не кожна програма має окремий крок збірки, а назва команди build сама не повідомляє повний перелік перевірок.
Тому фраза «збірка успішна» означає лише, що конкретний процес збірки завершився без виявленої ним помилки в тому середовищі. Вона не підтверджує, що користувач може забронювати правильний час, що збереження працює або що всі потрібні сценарії перевірені. Для цього потрібні докази поведінки, пов'язані з вимогою.
Синтаксис, тип, виконання й логічний дефект
Ці слова описують різні місця, де стає помітною проблема, і можуть перетинатися:
- Помилка синтаксису: парсер не читає структуру; наприклад, без коми між
"port": 4100і"mode": "preview"файл не є правильним JSON. - Помилка типу: операція не підходить значенню. Звернення до текстового методу в
undefinedможе спричинитиTypeErrorпід час виконання, тобто помилку типу може бути видно лише під час запуску. - Помилка виконання: код уже працює, але з'являється проблема, наприклад відмова сховища, відсутній файл або виняток.
- Логічний дефект: код читається й виконується, але порушує правило. Наприклад,
<=замість<може відхилити дозволений граничний час без повідомлення про синтаксичну помилку.
Повідомлення журналу може дати підказку, але не є доказом, що причина вже встановлена. Зіставте вхідне значення, умову, результат і правило, за яким приймається рішення.
Асинхронність: операція почалась, але ще не завершилась
Асинхронна операція — робота, результат якої стає відомий пізніше, наприклад збереження в сховище або запит до зовнішньої системи. Promise (обіцянка) у JavaScript — значення, що представляє майбутнє завершення або помилку такої операції. Якщо функція почала збереження й одразу повернула promise, запис ще не обов'язково збережений.
У наступному умовному прикладі припускаємо, що bookingStore.save повертає promise:
async function saveBooking(booking) {
const receipt = await bookingStore.save(booking);
return receipt;
}
async позначає асинхронну функцію; вона повертає promise. await чекає на результат bookingStore.save у межах цієї функції, а після успіху receipt отримує підтвердження. Якщо збереження відхиляє promise, помилка проходить далі, якщо код окремо її не обробить. Це не означає, що вся програма або весь сервер зупинився на цей час. Офіційна документація MDN пояснює promise та async/await як спосіб працювати з результатом і відмовою асинхронної операції (Using promises, async function).
Тут немає повного пояснення паралельності, черг або скасування операцій — це окремі теми пізніше. Для читання зміни агента достатньо помітити різницю між самим викликом функції і значенням, яке вона обіцяє повернути згодом.
Приклад 2. Змінити межу дозволеного часу й побачити побічний ефект
Ситуація. Вигаданий квиток каже: «Не дозволяти запис ближче ніж за 10 хвилин до початку». Для спрощення число позначає хвилини від початку дня; заплановані початки містяться в масиві.
Початковий код і зміна правила:
function canBook(startMinute, currentMinute, busyStarts) {
if (startMinute < currentMinute) {
return false;
}
for (const busyStart of busyStarts) {
if (startMinute === busyStart) {
return false;
}
}
return true;
}
Початковий варіант відхиляє час у минулому й уже зайнятий час. У ньому startMinute і currentMinute — числа, busyStarts — масив чисел, а результат — true або false.
Щоб виконати нову вимогу, потрібна межа рівно 10 хвилин: дозволено початок через 10 хвилин або пізніше, якщо цей час не зайнятий.
function canBook(startMinute, currentMinute, busyStarts) {
if (startMinute < currentMinute + 10) {
return false;
}
for (const busyStart of busyStarts) {
if (startMinute === busyStart) {
return false;
}
}
return true;
}
Умова відхиляє всі значення строго менші за currentMinute + 10. За currentMinute = 600:
| Запитаний час | Різниця | Масив зайнятих часів | Результат за правилом |
|---|---|---|---|
| 609 | 9 хвилин | [] |
false, надто близько |
| 610 | рівно 10 хвилин | [] |
true, граничний час дозволено |
| 620 | 20 хвилин | [620] |
false, час зайнятий |
Помилкова правка. Якщо агент змінить < на <=, значення 610 теж потрапить до відхилення, бо 610 <= 610. Код залишиться синтаксично правильним і може пройти збірку, але відхилить дозволений за вимогою граничний час. Саме тому корисно читати умову як словесне правило й перевіряти конкретні точки: «на 9 хвилин», «рівно 10» і «пізніше».
Результат і межі. Цей приклад пояснює одну межу й випадкову помилку біля неї. У справжньому сервісі хвилини потрібно трактувати разом із датою та часовим поясом; тривалість двох зустрічей може перетинатися, навіть якщо часи початку різні. Також час може зайняти інша людина вже після перевірки. Система має знову застосувати важливі правила під час збереження. Реальний контракт залежить від бізнесу, календаря й сховища; ця навчальна функція не є готовою системою бронювання.
Як читати зміну, запропоновану агентом
Контракт функції — це обіцяні їй вхідні дані, результат і помилки, на які мають зважати виклики. Коли агент змінює файл, спершу зв'яжіть diff (порівняння стану до й після) з вимогою:
- Які вимога й контракт функції змінилися: її параметри, результат або помилки?
- Які файли змінилися і чому; чи є змінений файл джерелом, чи його створює інший файл?
- Які тести покривають нову поведінку, важливі межі й невдалі сценарії, а що лишилося неперевіреним?
- Чи потрібні вимозі нові файли, налаштування й залежності?
Повний розбір diff та відповідальність за перегляд змін розглядаються в главі 50.
Тест, який перевіряє тільки рядок успіху, не обов'язково перевіряє відмову сховища, зайнятий час або граничне значення. З іншого боку, відсутність знайомства з певним синтаксисом не доводить, що код неправильний. Якщо призначення файла невідоме, шукайте, хто його читає або генерує, які команди згадано в README та що показують налаштування. Якщо доказів поки немає, правильний висновок — «потрібно з'ясувати», а не одразу «помилка».
Приклад 3. Агент змінив збірку замість джерела
Ситуація. У вигаданому дереві з src/booking-form.js і dist/app.bundle.js квиток просить змінити текст успішного підтвердження. Агент править тільки dist/app.bundle.js, додає пакет форматування часу до package.json і lockfile, а у своєму локальному перегляді бачить новий текст.
Що насправді підтверджує результат. Лише те, що переглянутий згенерований файл у цьому локальному оточенні показав новий текст. Це не доводить, що джерельний файл змінений, що наступна збірка збереже правку, що нова залежність потрібна або що час у підтвердженні правильний для потрібного часового поясу. Якщо команда повторно створить dist із src, ручна правка може зникнути. Якщо залежність не потрібна для вимоги, вона розширює перелік коду, який треба встановлювати й підтримувати, і змінює lockfile без поясненої користі.
Розбір змін. Спершу перевірте, чи саме dist/app.bundle.js є згенерованим виходом у цьому репозиторії: назва натякає, але доказом будуть скрипт збірки, коментар у файлі або інше посилання на генератор. Якщо джерело — src/booking-form.js, то правка має бути там, а вихід перебудовано прийнятим у проєкті процесом. Порівняйте, чи змінився контракт підтвердження: наприклад, текст має з'являтися тільки після збереження, а не після спроби запису. Перевірте, чи потрібен пакет для реалізації; якщо можна виконати вимогу без нього, залишення нової залежності потребує окремого пояснення. Для поведінки доречні перевірки успішного й невдалого збереження, а не лише вигляд локальної сторінки.
Коли рішення може бути іншим. Деякі проєкти навмисно зберігають згенеровані файли в репозиторії для розгортання або поширення. Не видаляйте dist і не відхиляйте його зміну лише за назвою. Спершу з'ясуйте, як проєкт будується та що очікує середовище випуску. GitHub описує .gitattributes-позначку linguist-generated як спосіб позначити певні файли згенерованими для відображення diff; це допоміжна підказка на GitHub, а не універсальний доказ джерела файла (GitHub Docs: Customizing how changed files appear).
Висновок прикладу. Локальна демонстрація — вузький доказ про один артефакт. Щоб оцінити зміну, потрібно встановити, яке джерело породжує результат, яка поведінка вимагається, що покривають відповідні тести та навіщо додана кожна залежність.
Підсумок
Репозиторій — не просто набір папок: код, дані, налаштування, перевірки та результат збірки в ньому мають різні ролі. Назви каталогів і розширення дають підказки, але README, імпорти, конфігурація й сценарії збірки — докази того, як влаштований саме цей проєкт.
Читаючи JavaScript, шукайте входи функції, умови, цикли, зовнішні значення й результат. Відрізняйте JSON, YAML і Markdown з даними чи поясненнями від коду, який приймає рішення. Маніфест описує прямі залежності та команди; lockfile фіксує вибране дерево залежностей. Розбір синтаксису, статична перевірка, збірка, запуск і тестування поведінки відповідають на різні запитання; успіх одного етапу не доводить правильність продукту.
Коли аналізуєте пропозицію агента, зв'яжіть diff із вимогою, контрактом, відповідними тестами, джерельними файлами та потребою в нових залежностях. Якщо доказів ще немає, відокремте невідоме від підтвердженої помилки; успіх окремого етапу сам по собі не доводить, що продукт працює для користувача.
Джерела та подальше читання
Усі вебсторінки нижче прочитані 23 вересня 2026 року. Динамічні онлайн-документи можуть змінюватися пізніше цієї дати.
- MDN Contributors, Grammar and types — JavaScript Guide, актуальна сторінка; прочитано 23 вересня 2026 року. Підтримує пояснення значень, типів, змінних, синтаксису й області видимості.
- MDN Contributors, Functions — JavaScript Guide і Control flow and error handling, актуальні сторінки; прочитано 23 вересня 2026 року. Підтримують визначення функції, параметра, результату, умов і циклів.
- MDN Contributors, JavaScript modules, актуальна сторінка; прочитано 23 вересня 2026 року. Підтримує роль імпорту й контекст залежності модульного завантаження від середовища.
- Node.js contributors, Modules: Packages, Node.js v26.10.0, документація Node.js v26.10.0; прочитано 23 вересня 2026 року. Підтримує пояснення точок входу пакета й того, як маніфест Node.js визначає завантаження модулів.
- MDN Contributors, Using promises і async function, актуальні сторінки; прочитано 23 вересня 2026 року. Підтримують пояснення майбутнього результату,
async,awaitі помилки. - Ecma International, ECMA-404: The JSON Data Interchange Syntax, 2-ге видання, грудень 2017 року; прочитано 23 вересня 2026 року. Підтримує роль JSON як синтаксису для обміну даними; точні правила стандарту, зокрема відсутність коментарів, визначаються специфікацією.
- YAML Language Development Team, YAML Ain’t Markup Language, version 1.2.2, редакція 1.2.2; прочитано 23 вересня 2026 року. Підтримує роль відступів і вкладеності YAML.
- CommonMark contributors, CommonMark Spec 0.31.2, версія 0.31.2; прочитано 23 вересня 2026 року. Підтримує короткий опис Markdown і застереження про відмінності розширень.
- MDN Contributors, Creating the content, актуальна навчальна сторінка; прочитано 23 вересня 2026 року. Підтримує визначення HTML і його роль у структурі сторінки.
- MDN Contributors, What is CSS?, актуальна навчальна сторінка; прочитано 23 вересня 2026 року. Підтримує визначення CSS і його роль у стилізації HTML.
- MDN, About MDN, офіційна сторінка історії проєкту; прочитано 23 вересня 2026 року. Підтверджує розшифрування MDN як Mozilla Developer Network у назві 2010 року; нині документація називається MDN Web Docs.
- Node.js contributors, Environment Variables, Node.js v26.10.0, документація Node.js v26.10.0; прочитано 23 вересня 2026 року. Підтримує опис змінних середовища та обмеження спільного формату
.env. - npm, Specifying dependencies and devDependencies і package-lock.json, чинна документація npm; прочитано 23 вересня 2026 року. Підтримує ролі груп залежностей і призначення lockfile; нюанси залежать від версії npm.
- Microsoft, TypeScript for the New Programmer, чинний TypeScript Handbook; прочитано 23 вересня 2026 року. Підтримує статичну перевірку та відмінність від поведінки під час виконання.
- GitHub Docs, About the repository README file, Ignoring files і Customizing how changed files appear, актуальні сторінки; прочитано 23 вересня 2026 року. Підтримують опис README, ігнорування ще не відстежуваних файлів і GitHub-позначки для згенерованих матеріалів.
- Sindre Sorhus, p-limit v7.3.3 README, package.json і test.js, тег v7.3.3; прочитано 23 вересня 2026 року. Використано як невеликий відкритий приклад README, маніфесту й тестів; конкретну структуру не подано як загальний стандарт. Жоден код чи тест цього проєкту не запускався.