1. Портфоліо як вітрина довіри
Портфоліо розробника-початківця часто виглядає так: десять репозиторіїв, у кожному final_final2.go, а в README гордо написано «Мій класний проєкт». На співбесіді це не додає довіри, бо не видно головного: як поводиться ваш код, які в нього контракти і чи можна йому довіряти. Портфоліо — це не про кількість, а про демонстрацію якості.
Уявіть, що ваш проєкт — це маленький прилад. Користувач не зобов’язаний знати, як усередині влаштовані шестерні. Йому важливо, чи вмикається прилад, що показує дисплей, що станеться у разі помилки і чи не зламається він через неправильну кнопку. У програмному забезпеченні «дисплей» — це контракт CLI/HTTP, а «не зламається» — це тести, акуратні помилки й передбачуваність.
Щоб портфоліо мало «форму», корисно тримати в голові трійку: демонстрація → гарантії → відтворюваність. Демонстрація відповідає на запитання «що це робить і навіщо», гарантії — «чому цьому можна довіряти», відтворюваність — «як це запустити й перевірити». У хорошому проєкті це видно за 2–3 хвилини, без режиму археолога.
Мінімальний каркас демо для навчального застосунку
Візьмемо умовний підсумковий проєкт курсу — менеджер задач (нехай називається tasker), у якого є CLI та HTTP API. У демо ви майже завжди показуєте один сценарій успіху і один-два сценарії помилки, але через контракти.
Невеликий фрагмент правильного входу в CLI, який добре виглядає в портфоліо, бо поведінка зосереджена в одному місці й легко пояснюється:
package main
import (
"os"
)
func main() {
os.Exit(run(os.Args[1:]))
}
І поруч — точка, де ви свідомо керуєте зовнішнім контрактом CLI: аргументи → код виходу:
package main
import (
"errors"
"fmt"
"os"
)
var ErrUsage = errors.New("використання")
func run(args []string) int {
if len(args) == 0 {
fmt.Fprintln(os.Stderr, "використання: tasker <command>")
return 2
}
return 0
}
Це маленький приклад, але він показує зрілість: ви не розкидаєте os.Exit по всьому коду, а тримаєте зовнішній контракт в одному місці.
2. README як інтерфейс проєкту
README — це не твір на тему «як я провів літо з Go». Це інструкція, яка допомагає людині швидко відповісти на чотири запитання: що робить проєкт, як його запустити, як перевірити якість, де подивитися приклади. Якщо README не відповідає на ці запитання, то це не README, а декоративна листівка.
У портфоліо особливо важлива швидкість розуміння: чим швидше людина, яка перевіряє, побачить сигнал «проєкт акуратний», тим більша ймовірність, що вона взагалі читатиме код. Іронія в тому, що хороший README часто важливіший за витончену архітектуру, бо архітектура без читача — це як жарт без аудиторії: автору смішно, решті — тиша.
Хороший прийом — писати README у стилі «quickstart + контракт». Нижче приклад фрагмента, який можна тримати як орієнтир (це текст, але структура важливіша за конкретні слова):
tasker — мінімальний менеджер задач (CLI + HTTP).
Швидкий старт:
go test ./...
go run ./cmd/tasker help
Контракт CLI:
stdout — дані
stderr — помилки/використання
коди виходу: 0 — ok, 2 — використання, 1 — виконання
Контракт HTTP:
JSON, Content-Type: application/json
помилки: { "error": { "code": "...", "message": "...", "fields": {...} } }
Зверніть увагу: тут немає «ми використали мікросервісну архітектуру з квантовими енумами». Є те, що можна перевірити вручну за хвилину.
Example-тести як частина портфоліо
Звичайний приклад у README швидко старіє. Example-тест компілюється і може перевірятися через go test, тому він є «живим прикладом». Це один із найбільш недооцінених способів показати якість: ви не переконуєте словами — ви показуєте перевірюваний сценарій.
Наприклад, ви хочете, щоб функція парсингу ID була зрозуміла і людині, і тестовому раннеру:
package tasker
import (
"fmt"
"strconv"
)
func ExampleParseID() {
id, err := strconv.Atoi("42")
fmt.Println(id, err == nil) // 42 true
// Output: 42 true
}
У портфоліо це працює як маленька гарантія: «я не тільки написав код, я зафіксував поведінку так, що вона не „попливе“».
3. Код-ревʼю як навичка
Код-ревʼю багато новачків сприймають як іспит і думають: «зараз мене сваритимуть». На практиці код-ревʼю — це спосіб зменшити ризик помилок і підвищити якість комунікації. Це спільне проєктування. Іноді воно справді неприємне, але найчастіше неприємне не ревʼю, а відсутність звичок: незрозумілий diff, хаотичні зміни, немає тестів, немає пояснення «навіщо».
Хороша новина: код-ревʼю — це навичка, яку можна прокачувати. Ще краща новина: ви можете почати робити ревʼю вже сьогодні, навіть якщо у вас немає команди, бо існує дієва техніка — саморевʼю. Ви робите PR «для себе», проходите чек якості, і лише потім показуєте його комусь. Це різко знижує кількість зауважень у стилі «Ну, gofmt-то можна було зробити?».
У портфоліо код-ревʼю видно опосередковано: за історією комітів, за тим, наскільки зміни атомарні, за наявністю тестів і за тим, як ви оформлюєте помилки. Навіть якщо ніхто не лишав вам коментарів на GitHub, проєкт може виглядати так, ніби «пройшов ревʼю у досвідченого інженера».
Маленький ритуал саморевʼю на прикладі зміни в tasker
Припустімо, ви додали валідацію заголовка задачі. Поганий варіант часто виглядає так: функція є, помилки різного стилю, тестів немає, а валідація розмазана по трьох шарах.
Акуратна версія починається з маленької функції, яку легко тестувати:
package tasker
import "errors"
var ErrTitleEmpty = errors.New("заголовок порожній")
func ValidateTitle(title string) error {
if title == "" {
return ErrTitleEmpty
}
return nil
}
Так, це проста функція. Але саме такі речі формують смак до коду: маленькі, читабельні, тестовані шматочки.
І поруч — невеликий табличний тест, який закріплює контракт:
package tasker
import "testing"
func TestValidateTitle(t *testing.T) {
if err := ValidateTitle(""); err == nil {
t.Fatalf("очікували помилку")
}
if err := ValidateTitle("купити молоко"); err != nil {
t.Fatalf("неочікувана помилка: %v", err)
}
}
У ревʼю такий код зазвичай коментують мало. Не тому що він «ідеальний», а тому що він не потребує здогадок.
Чек якості як звичка
Важливо, щоб якість не залежала від настрою («сьогодні я молодець, завтра втомився»). Тому більшість команд перетворюють якість на ритуал. Ви вже знаєте інструменти: форматування, тести, аналізатори. Далі питання не «що запускати», а «як зробити це звичкою».
Тут корисна одна проста думка: чим менше ручних дій, тим вищий шанс, що ви їх справді зробите. Навіть в одиночному проєкті можна завести одну команду, яку ви запускаєте перед пушем. Іноді це роблять через Makefile, іноді через скрипт, іноді через налаштування запуску в IDE — не принципово. Принципово, щоб «перевірити себе» було так само легко, як відкрити браузер і відволіктися.
4. Читання вихідного коду стандартної бібліотеки
Читати вихідний код стандартної бібліотеки звучить як щось, що роблять люди в темних плащах, коли компілятор вередує. Насправді це дуже практична звичка. Стандартна бібліотека Go — це величезний збірник рішень: як оформлювати помилки, як проєктувати API навколо інтерфейсів, як писати код, який житиме роками, і чому простота іноді досягається не магією, а дисципліною.
Читати stdlib треба не з думкою «я зараз усе зрозумію», а з думкою «я зараз зрозумію один невеликий шматок і візьму з нього одну хорошу звичку». Це як спортзал: ви не приходите туди один раз і не стаєте супергероєм. Ви приходите регулярно, і за кілька місяців раптом помічаєте, що ваш код став спокійнішим і чистішим.
Є ще один бонус: коли ви читаєте stdlib, ви перестаєте вірити міфам. Побачили в реальному коді, що помилки обгортають контекстом, що інтерфейси маленькі, що багато уваги до меж вхідних даних, — і магічне мислення починає зникати.
Як знайти вихідні коди пакета
У Go це робиться доволі прямолінійно: у вас є GOROOT, і там лежить стандартна бібліотека. Якщо ви використовуєте IDE, найчастіше можна просто перейти до визначення для net/http або encoding/json і потрапити у вихідний код. Якщо ви любите термінал (або IDE показує шлях), можна орієнтуватися на GOROOT.
Якщо ви хочете «відчути», що Go — це не лише мова, а й інструментальна екосистема, корисно знати, що в команди go багато режимів і файлів навколо проєкту. Наприклад, workspaces (go.work) — один зі способів керувати кількома модулями разом; він не обов’язковий новачку щодня, але сам факт, що це задокументовано й підтримано інструментами, — частина «культури Go».
А ще корисно пам’ятати, що деякі команди — це окремі кроки процесу. Наприклад, go generate існує, але він не «вбудований магічно» у збірку: його потрібно запускати явно, і це принципово для відтворюваності. Навіть якщо ви не використовуєте go generate просто зараз, розуміння цієї філософії допомагає писати проєкти без прихованої магії.
Що читати в stdlib, якщо ви новачок
Не потрібно починати з runtime. Починати краще з пакетів, які ви вже використовували в курсі: errors, fmt, io, bufio, net/http, encoding/json, context, sync. Там ви побачите знайомі ідеї в ідеальній подачі.
Дуже корисна техніка читання: йти від інтерфейсу до реалізації. Спочатку дивитеся, як виглядає контракт (наприклад, io.Reader), потім — хто його реалізує, потім — які обгортки й декоратори навколо нього побудовані. Це повторює наш курс: спочатку контракт, потім реалізація, потім тестованість.
Мініпрактика: декоратор у стилі stdlib
Коли ви читаєте stdlib, особливо пакети навколо io, виникає відчуття: «та вони все загорнули в щось, що реалізує інтерфейс». І це правда: у Go дуже люблять маленькі обгортки, які нічого не ламають, а додають поведінку. Це ідеальне тренування для розвитку: ви вчитеся розширювати систему не переписуванням, а композицією.
Зробімо невеликий крок у нашому tasker: додамо обгортку над io.Writer, яка рахує, скільки байтів ми записали. Це корисно, наприклад, для логів, метрик або тестів, і це рівно той стиль мислення, який ви постійно зустрічатимете в стандартній бібліотеці.
package tasker
import "io"
type CountingWriter struct {
W io.Writer
N int
}
func (cw *CountingWriter) Write(p []byte) (int, error) {
n, err := cw.W.Write(p)
cw.N += n
return n, err
}
Тепер ви можете використати це в будь-якому місці, де у вас є writer (stdout, файл, буфер). І це дуже «по-Go»: мінімум магії, максимум прозорості.
Наприклад, ви хочете в CLI порахувати, скільки байтів ви вивели користувачу:
package main
import (
"fmt"
"os"
"example.com/tasker"
)
func main() {
cw := &tasker.CountingWriter{W: os.Stdout}
fmt.Fprintln(cw, "hello") // hello
fmt.Println("байти:", cw.N) // байти: 6
}
Так, приклад іграшковий. Але він вчить трьох дорослих речей: інтерфейсів, композиції та спостережуваності. І в stdlib таких маленьких «цеглинок» дуже багато — саме тому читання її вихідного коду так добре прокачує мозок.
5. Ритм зростання
Майже всі плани розвитку ламаються об одне й те саме: людина ставить собі ціль «вивчити все», а за тиждень починає ненавидіти і себе, і Go. Щоб не повторювати цю класичну трагікомедію, корисно прийняти просту ідею: розвиток має бути маленьким, регулярним і вимірюваним. Не в сенсі «метрики KPI», а в сенсі «я можу чесно сказати, що зробив крок».
Дуже зручний формат — тижневий ритм, де у вас є кілька повторюваних активностей: трохи покращити проєкт (портфоліо), трохи потренувати ревʼю, трохи почитати хороший код. Важлива не кількість, а стійкість. Якщо ви робите по 20 хвилин, але щотижня — це краще, ніж один «марафон на вихідних» раз на два місяці.
Нижче приклад ритму, який добре лягає на реальність, навіть якщо ви навчаєтеся паралельно з роботою чи навчанням:
| Звичка | Час | Що вважається «зробив» |
|---|---|---|
| Мініпокращення проєкту | 30–60 хв/тиждень | один PR: тест, виправлення багу, маленька фіча |
| Саморевʼю | 10–15 хв/PR | прогнав перевірки, перечитав diff |
| Читання stdlib | 20–30 хв/тиждень | один файл / одна функція + нотатка |
| Документація | 15 хв/тиждень | покращив README або додав Example-тест |
Зверніть увагу: тут немає пункту «вивчити Kafka». Не тому що Kafka погана, а тому що без ритму ви не вивчите навіть fmt.Printf — буде лише відчуття провини й вкладка з курсом, відкладена «на потім».
І ще одна важлива думка: стежте за змінами платформи. Go розвивається, інструменти змінюються, з’являються нові дефолти. Наприклад, у Go 1.25 змінилася поведінка GOMAXPROCS у контейнерах: рантайм почав враховувати CPU limits за замовчуванням, щоб уникнути неприємних затримок через throttling. Вам не потрібно просто зараз «глибоко вивчати контейнери», але корисно мати звичку читати такі нотатки й розуміти, що платформа жива.
6. Типові помилки в особистій стратегії зростання
Помилка № 1: робити портфоліо «про кількість», а не «про довіру».
Коли у вас багато репозиторіїв, але жоден не має зрозумілого quickstart, тестів і ясних контрактів, людині, яка перевіряє, важко повірити у ваш рівень. Набагато переконливіше виглядає один проєкт, який запускається з першої спроби, має передбачувані помилки, тести й зрозумілий README.
Помилка № 2: сприймати код-ревʼю як покарання.
Якщо ви чекаєте на ревʼю «як на вирок», ви починаєте ховати зміни, робити їх дрібними й беззмістовними або, навпаки, заливати величезні коміти «поки не передумав». Здорова модель інша: ревʼю — це спільне зниження ризику. Саморевʼю перед відправленням — найкращий спосіб зробити ревʼю спокійним і корисним.
Помилка № 3: читати stdlib як роман і мучитися.
Спроба зрозуміти одразу весь net/http або encoding/json може демотивувати. Набагато ефективніше читати маленькими порціями: один тип, одна функція, один тест. Сенс у накопиченні звички й професійного смаку, а не в разовому подвигу.
Помилка № 4: плутати «знаю» і «можу показати».
На співбесіді та в роботі цінується не те, що ви «приблизно розумієте», а те, що ви можете швидко відтворити: запустити тести, показати демо, пояснити контракт помилок. Тому портфоліо має бути показовим: команди запуску, сценарій і очікуваний результат.
Помилка № 5: покращувати якість «коли буде час».
Якщо якість не вбудована в процес, вона не з’явиться. У якийсь момент проєкт починає жити, баги накопичуються, і ви відкладаєте тести «на потім». Набагато дешевше — маленький ритуал: форматування, тести, статичний аналіз, і вже потім коміт.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ