JavaRush /Курси /Swift SELF /README для LibraryCLI

README для LibraryCLI

Swift SELF
Рівень 50 , Лекція 2
Відкрита

1. Навіщо взагалі потрібен README

Щойно ви створили CLI-застосунок, дуже легко потрапити в пастку авторського погляду. Вам здається, що все й так зрозуміло: як запускати проєкт, які є команди, що вони приймають на вхід і що друкують у відповідь. Але це зрозуміло вам, тому що ви щойно писали код, бачили структуру проєкту й памʼятаєте всі домовленості.

Користувач опиняється в зовсім іншій ситуації. Він уперше відкриває репозиторій. У нього немає ні вашої памʼяті, ні вашого контексту, ні телепатичного звʼязку з main.swift. Для нього проєкт — це просто папка з файлами й одне запитання: «Як цим користуватися?»

Саме тут зʼявляється README.

README — це перша точка входу до проєкту. Він потрібен не для краси й не для галочки, а для дуже практичного завдання: допомогти людині швидко зрозуміти:

  • що це за програма;
  • як її запустити;
  • які команди вона підтримує;
  • який ввід вважається коректним;
  • що застосунок виводить у відповідь.

Якщо цього немає, користувач починає вгадувати. А коли користувач вгадує, він майже завжди помиляється. Він пробує не ту команду, не той формат аргументів, не ту назву виконуваного файла — і за кілька хвилин уже думає, що проблема в проєкті, хоча насправді проблема в тому, що інструкції немає.

Тому README зручно сприймати як коротку й чесну інструкцію з використання. Не як довгий трактат про нутрощі проєкту, а як зрозумілий путівник: ось що це таке, ось як запустити, ось як користуватися.

Для CLI-інструментів це особливо важливо. У графічного застосунку є кнопки, вікна й підказки на екрані. У CLI майже все спілкування з користувачем відбувається через текст. Отже, README тут відіграє роль першої й дуже важливої частини інтерфейсу.

2. README — це «контракт із користувачем»

Коли ми вже зрозуміли, навіщо README потрібен загалом, можна зробити наступний крок.

README — це не просто «трохи тексту про проєкт». Для CLI-інструмента README краще сприймати як контракт із користувачем.

Уявіть, що LibraryCLI — це маленький автомат із кнопками. README в такому разі — це наліпка над панеллю керування: що робить кожна кнопка, які дії взагалі підтримуються, який ввід вважається нормальним і що станеться у відповідь.

Тобто README має знімати найважливіші запитання ще до того, як людина полізе у вихідний код:

  • як правильно запускати застосунок;
  • як називається команда;
  • які аргументи вона приймає;
  • як виглядає коректний ввід;
  • який вивід вважати нормальним;
  • що вважається помилкою.

У хорошого README є дуже важлива властивість: людина може почати користуватися застосунком без читання коду. Це і є головний тест якості. Якщо для запуску CLI потрібно відкрити Package.swift, подивитися main.swift, здогадатися про назви команд і ще розібратися в парсері аргументів, значить README не виконує свою роботу.

Водночас README не повинен перетворюватися на роман на сорок сторінок. Його завдання — не описати весь світ проєкту, а дати користувачеві коротку, точну й робочу картину. Достатньо настільки детально, щоб людина змогла впевнено запустити й використовувати застосунок, але без зайвої теорії та екскурсії архітектурою.

Саме тому далі в лекції ми будемо будувати README не як «вільний текст про проєкт», а як набір конкретних розділів: запуск, Usage, список команд, формат введення й виведення, приклади.

3. Найважливіший розділ «Як запустити»

Цю частину дуже легко недооцінити. Автор проєкту думає: «ну, запуск же очевидний». Користувач думає: «а що у вас вважається очевидним: Xcode? SwiftPM? Docker? Фаза Місяця?». Тому в README ми пишемо запуск так, ніби людина, яка читає, ніколи не бачила ваш проєкт.

Для нашого навчального проєкту на SwiftPM базовий запуск виглядає так:


swift run LibraryCLI

SwiftPM прямо пропонує такий стиль запуску для CLI-проєктів: «build/run/test» — основний цикл, а swift run запускає виконуваний таргет за назвою.

У README важливо не просто показати команду, а й мінімально пояснити контекст. Наприклад: «з кореня репозиторію», «потрібен Swift toolchain», «якщо команда не знайдена — перевірте, що Swift установлено».

При цьому ми не заглиблюємося в тонкощі середовища та встановлення SDK — це окрема тема. Ми просто даємо користувачеві швидкий старт.

4. Usage: робимо «міні-граматику» команд

Коли README складається лише з прикладів, користувач опиняється в ситуації «вгадай правило за трьома прикладами». Іноді це працює, але частіше перетворює використання CLI на квест. Тому майже в кожного нормального інструмента є блок Usage: один рядок, який задає форму команди.

Тут зручно мислити як парсер. Ми вже говорили про граматику команд: є команда, є аргументи, є опції. Usage — це опис того, що CLI взагалі приймає, якщо дивитися зверху вниз.

Ось розумний мінімум для LibraryCLI:

Usage:
  LibraryCLI <command> [arguments]

Run:
  swift run LibraryCLI <command> [arguments]

Зверніть увагу на два рівні. Перший — як команда виглядає загалом, якщо бінарник уже встановлено. Другий — як ми запускаємо її саме в нашому навчальному проєкті через SwiftPM. Цей другий варіант спирається на стандартний підхід swift run <executable>.

Невелика блок-схема: де живе Usage

Інколи допомагає візуалізація, особливо якщо ви тільки починаєте.

flowchart TD
    A[Користувач відкриває README] --> B[Бачить розділ «Використання»]
    B --> C[Копіює приклад команди]
    C --> D[Запускає: swift run LibraryCLI ...]
    D --> E[Отримує передбачуваний результат]

Сенс простий: README має скорочувати шлях від «хочу спробувати» до «вийшло».

5. Команди: як описувати список команд

Список команд — серце README для CLI. Але є тонкий момент: якщо ви просто напишете «Команди: add, remove, list», це виглядатиме як меню ресторану без опису страв. А якщо ви напишете по абзацу на кожну команду, README розростеться, і його перестануть читати.

Хороший баланс — описувати команди структуровано: назва, аргументи, що робить, приклад. Для читабельності чудово працює таблиця: вона компактна й дає «сканований» огляд.

Наприклад, на поточному етапі курсу ми можемо тримати базовий набір:

Команда Аргументи Призначення Приклад
help
Показує довідку
swift run LibraryCLI help
version
Показує версію
swift run LibraryCLI version
add
--title, --author, --year
Додає книжку до поточної сесії
swift run LibraryCLI add --title "Dune" 
--author "Frank Herbert" --year 1965
list
Показує книжки
swift run LibraryCLI list
find
--query
Шукає за назвою чи автором
swift run LibraryCLI find --query "dune"
remove
--id
Видаляє книжку за ідентифікатором
swift run LibraryCLI remove --id B1

Зверніть увагу: ми не обіцяємо зберігання у файлі або JSON-експорт, тому що якщо цього не реалізовано — README почне брехати. README, який бреше, перестає бути документацією і перетворюється на фанфік.

6. Формат даних: «що таке книжка» в термінах CLI

Коли люди чують «формат даних», вони часто думають про JSON, CSV та інші дорослі речі. Але на нашому поточному етапі все простіше: формат даних — це, по суті, відповідь на два запитання.

Перше запитання звучить так: «Які поля в сутності книжки важливі для користувача команди?» Друге запитання звучить так: «Як ці поля подані у вводі й виводі, щоб це було стабільно й передбачувано

Нехай наша предметна модель книжки поки що мінімальна: id, title, author, year. У README ми фіксуємо, що користувач вводить title/author/year, а id генерується програмою і використовується для видалення.

Стабільний вивід: ключ-значення замість «красивостей»

Є спокуса зробити вивід «як у застосунку», з рамками, емодзі та мистецтвом ASCII. Це весело перші 30 секунд, але погано для передбачуваності. Чим простіший формат, тим легше його читати очима й парсити скриптами.

Наприклад, для list можна домовитися про такий вивід:

id=B1 title="Dune" author="Frank Herbert" year=1965
id=B2 title="Clean Code" author="Robert C. Martin" year=2008

Так, це не виставка дизайну. Зате це залізобетонний контракт. Якщо ви потім захочете покращити формат, ви хоча б будете розуміти, що ламаєте зовнішній API.

Формат введення: що ми вважаємо валідним

README має явно сказати, який ввід вважається коректним. Наприклад, year — ціле число, title і author — рядки, краще в лапках, якщо є пробіли. Якщо ваш CLI підтримує лапки в токенізації — а ми це обговорювали на парсингу — README має показати приклади саме з лапками.

І тут важлива педагогічна думка: README допомагає не тільки користувачеві, а й вам як розробнику. Він змушує формалізувати правила. Якщо правило не можна пояснити в README, значить і в коді воно теж буде «плавати».

7. Приклади команд: робимо їх копійованими і самодостатніми

Приклади команд — це те місце, де користувач найчастіше й працює. Він не читає всю документацію, він робить Copy → Paste → Enter. Отже, приклади мають бути такими, щоб цей сценарій працював.

Тому приклад має мати три якості.

Перша якість: приклад запускається «як є» — принаймні синтаксично. Друга якість: приклад показує очікуваний вивід, хай навіть частково. Третя якість: приклад використовує єдину назву команди — знову LibraryCLI.

Ось приклад блоку README — саме того контенту, який ви справді покладете в README.md:

```md

## Використання

Запуск через SwiftPM (з кореня репозиторію):

```bash
swift run LibraryCLI <command> [arguments]
```

## Приклади

Додати книжку:

```bash
swift run LibraryCLI add --title "Dune" --author "Frank Herbert" --year 1965
```

Список книжок:

```bash
swift run LibraryCLI list
```

Приклад виведення:

```text
id=B1 title="Dune" author="Frank Herbert" year=1965
```

```

Зверніть увагу: ми не зобовʼязані показувати «Building for debugging…» та інші рядки SwiftPM, тому що це шум і може відрізнятися між середовищами. Ми показуємо те, що стосується контракту нашого застосунку.

8. Коди завершення: фіксуємо «0 / не 0» без нумерології

Користувацький досвід у терміналі — це не тільки текст, а й код завершення процесу. На цьому етапі достатньо базового принципу: 0 — успіх, будь-яке ненульове значення — помилка. І цього достатньо, щоб README став чеснішим.

У README це можна описати коротко, без таблиць:

## Коди завершення

`LibraryCLI` повертає код завершення `0` у разі успіху та ненульовий код у разі помилки.

Чому без таблиці? Тому що якщо ви сьогодні вигадаєте «2 — помилка парсингу, 3 — помилка домену…», а завтра зміните архітектуру помилок, README миттєво стане сміттям. Таблиці кодів завершення мають сенс, коли схема стабілізована і закріплена в проєкті. Зараз ми тримаємося мінімуму: «успіх/помилка».

Надмірна деталізація кодів завершення потрібна лише тоді, коли схема вже стабілізована і закріплена в проєкті. Зараз тримаємося мінімуму: «успіх/помилка».

9. Як не розсинхронізувати README і help

Дуже типова проблема CLI-проєктів: README каже одне, а команда help — інше. Людина читає README, запускає help, і починається філософська дискусія: яка з двох реальностей справжня?

Щоб цього уникати, корисно зробити так, щоб help-текст був максимально близьким до README за форматом і назвами. І ще краще — щоб він жив у коді як константа або невеликий генератор, який легко оновлювати.

Давайте додамо в наш LibraryCLI невелике «джерело істини» для довідки.

Модель команд: список в одному місці

Невеликий фрагмент коду, який розширює наш застосунок і водночас допомагає README: список команд можна взяти з enum Command.

import Foundation

enum Command: String, CaseIterable {
    case help, version, add, list, find, remove
}

Код короткий, читабельний, і найголовніше — якщо ви додасте нову команду, ви не забудете про неї в одному з місць: README, help або парсері.

Генерація help-тексту з єдиним неймінгом

Тепер зберемо help-текст так, щоб він повторював те, що ми пишемо в README: LibraryCLI, структуру Usage, список команд.

import Foundation

struct HelpText {
    static let usage = """
    Використання:
      LibraryCLI <command> [arguments]

    Запуск:
      swift run LibraryCLI <command> [arguments]
    """
}

Якщо хтось запитає «чому рядок багаторядковий?», ви можете гордо відповісти: «тому що так краще читається». І це буде правдою.

Друк списку команд із CaseIterable

Тепер додамо функцію, яка друкує команди. Тут важливий нюанс: ми друкуємо їх стабільно і простим текстом.

import Foundation

func printCommands() {
    print("Команди:")
    for cmd in Command.allCases {
        print("  \(cmd.rawValue)")
    }
}

Якщо printCommands() викличеться, вивід буде приблизно таким:

Команди:
  help
  version
  add
  list
  find
  remove

І ось це вже можна копіювати в README як «офіційний список».

Міні-main: команда help

Покажемо крихітну точку входу, яка обробляє help. Ми зараз не занурюємося в повноцінний парсер — він у вас уже є в проєкті — просто демонструємо принцип.

import Foundation

let args = Array(CommandLine.arguments.dropFirst())
let cmd = args.first ?? "help"

if cmd == "help" {
    print(HelpText.usage)
    printCommands()
}

Так, це «наївний main», але він показує важливу ідею: help-повідомлення й список команд можна тримати поруч, і README буде простіше підтримувати.

10. Шаблон README для LibraryCLI

Нижче — приклад того, як може виглядати ваш README.md. Він спеціально зроблений «без зайвого»: короткий, але з контрактом.

```md
# LibraryCLI

Невеликий інструмент командного рядка для керування списком книжок.

## Запуск

З кореня репозиторію:

```bash
swift run LibraryCLI <command> [arguments]
```

## Використання

```text
LibraryCLI <command> [arguments]
```

## Команди

```text
help
version
add --title <string> --author <string> --year <int>
list
find --query <string>
remove --id <string>
```

## Формат виведення

`list` друкує одну книжку на рядок:

```text
id=<ID> title="<TITLE>" author="<AUTHOR>" year=<YEAR>
```

## Приклади

Додати:

```bash
swift run LibraryCLI add --title "Dune" --author "Frank Herbert" --year 1965
```

Список:

```bash
swift run LibraryCLI list
```

Приклад виведення:

```text
id=B1 title="Dune" author="Frank Herbert" year=1965
```

## Коди завершення
```text
`0` означає успіх. Будь-який ненульовий код означає помилку.
```
```

Ви можете розширювати цей README, але важливо тримати структуру: запуск → Usage → команди → формат → приклади. Тоді документ читається як інструкція, а не як потік свідомості.

11. Типові помилки під час написання README для CLI

Помилка №1: README і реальність розходяться.
Найгірша ситуація — коли README обіцяє можливість, якої немає, або показує приклад команди, яка вже не парситься. Це відбувається непомітно: ви змінили парсер, додали новий обовʼязковий аргумент, а README не оновили. За тиждень ви самі будете тим самим користувачем, який запускає приклад із README й отримує помилку, а потім підозрює, що проєкт «зламався сам». На практиці рятує тільки дисципліна: змінюємо поведінку CLI — одразу правимо README й help-текст.

Помилка №2: приклади не можна копіювати.
Дуже часта дрібниця: у прикладі написано LibraryCli замість LibraryCLI, або приклад передбачає, що ви вже нібито зібрали бінарник, але не пояснює як. Користувач копіює, отримує «command not found», і на цьому знайомство закінчується. Тому приклади краще писати у вигляді команд swift run LibraryCLI ..., тому що це стандартний шлях запуску executable у SwiftPM.

Помилка №3: надто «красивий», але нестабільний вивід.
Якщо вивід оформлено як «табличка із символів», його приємно дивитися, але важко використовувати і майже неможливо гарантувати стабільність. У результаті ви випадково змінюєте кількість пробілів — і ламаєте всім парсинг або власні тести, якщо робите їх через порівняння рядків. Стабільний plain text із ключами (id=... title=...) виглядає нудно, зате працює роками.

Помилка №4: немає опису формату даних, лише слова «додає книжку».
Фраза «додає книжку» не відповідає на запитання «які поля?», «як виглядає id?», «як вводити рік?», «чи можна пробіли?». У результаті користувач починає експериментувати, а CLI перетворюється на генератор сюрпризів. Навіть один маленький розділ «Формат виведення» різко зменшує кількість запитань і помилок.

Помилка №5: README перетворюється на опис внутрішностей проєкту.
Інколи хочеться розповісти про targets, шари, протоколи, Repository, Service, DI — і це справді цікаво, особливо нам із вами. Але README для користувача CLI має описувати використання, а не влаштування. Щойно README починає пояснювати архітектуру замість команд і форматів, він перестає виконувати свою роботу: допомагати запустити й застосувати інструмент.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ