Скетч поливальника я открыл через полгода после написания — и минут двадцать не мог ответить на вопрос, почему в условии стоит число 400, откуда взялась переменная t3 и что делает блок из шести строк без единого комментария. Код работал. Я просто не смог его прочитать — собственный, написанный своими руками. Вечером того же дня я советовал на форуме новичку прислать скетч «в нормальном виде», и поймал себя на том, что на моём диске живут файлы с отступами в два пробела, восемь пробелов и один таб — вперемешку в одном исходнике. Дифф я тогда не смотрел: смотрел на код и угадывал, где какой отступ заканчивается.
Этот урок — про гигиену, за которую не платят ни байтом памяти, ни микросекундой: автоформат IDE по Ctrl+T (Cmd+T на macOS), имена, которые несут смысл, константы заглавными, короткие строки и комментарии, отвечающие на вопрос «почему», а не «что». Поведение той же мигалки из первого урока не изменится — и именно поэтому такие мелочи откладывают на никогда. А потом читают свой код при выключенном свете и на ощупь. Плата для правил ниже не нужна: они работают и в симуляторе, и в блокноте, и в чате, где вы обсуждаете чужой кусок.
Автоформат: одна кнопка вместо вечного спора
Ctrl+T в Windows и Linux, Cmd+T на macOS — команда Auto Format выравнивает отступы, пробелы вокруг операторов и переносы строк по одному фиксированному набору правил. Под капотом у IDE 2 — clang-format, тот же движок, что наводит порядок в больших C++ проектах, поэтому результат воспроизводим: один и тот же скетч два человека с разных континентов отформатируют одинаково. Настраивать ширину отступа или стиль скобок в настройках среды не выйдет — набор правил ровно один, и это решение, а не недоработка: единый формат всегда был ценнее лично моего любимого.
Честно про границы: автоформат не переименовывает переменные, не выносит повторяющийся код в функцию и не находит сбой в логике. Его вселенная — пробелы, отступы и переносы, всё. Простыню из повторов он оставит повтором, и в этом смысл: на третьем повторе вы сами захотите вынести кусок в функцию, форматирование лишь поднимает вопрос на поверхность. Мой ритм простой: Ctrl+T перед каждым сохранением, две секунды. Отдача выходит на размере проекта: git-разница показывает только настоящие правки вместо сдвигов пробелов, а бэкап проекта в git превращается в историю решений, а не в свалку редакторских шума. Если когда-нибудь переберётесь в серьёзную среду, там стиль уже настраивается — чем она отличается и чем берёт, я сравнивал на живом примере.
Имена, константы, строки: договор с собой из трёх пунктов
Официальный API Arduino написан в camelCase: digitalWrite, analogRead, attachInterrupt — ни одной функции в snake_case ядро не содержит, и мои скетчи продолжают традицию: intervalMayaka, prochteniePoroga. Разница между t3 и prochteniePoroga не в длине, а в способности через полгода ответить на вопрос «что это». Константы пишу заглавными с подчёркиваниями — PERIOD_MS, POROG_NIGHT: их видно с первого взгляда и рука не тянется менять их посреди loop(). И единицы прямо в имя: delayUs против delayMs экономят один сюрприз с множителем на тысячу. Проверка на живость за десять секунд: произнесите имя вслух — если в нём есть предмет и действие, оно работает; если буква с цифрой, это шифр, оставленный себе в наследство.
Пробелы против табов — спор, который предлагаю закрыть одной фразой: выбирайте что-то одно и не смешивайте никогда. IDE по умолчанию ставит табы, автоформат их сохраняет, и пока вы не полезли руками с пробелами, конфликтов не существует. Грабля начинается в командной работе: половина правок делается в IDE, половина в блокноте или веб-редакторе — и git показывает весь файл как изменённый, хотя поменялась одна строка. Историю я набил один раз на всю жизнь: файл с отступом «восемь пробелов после таба» открылся у коллеги кирпичами — его редактор считал ширину по-своему, и колонки поехали. О цене путаницы в истории правок — отдельное предупреждение ниже.
Длину строки держу в пределах сотни знаков. Причины утилитарные: монитор порта переносит текст по-своему, чтение длинной строки на телефоне превращается в горизонтальный скролл, а в git-разнице две версии строки на 180 знаков рядом не показываются. Условие, развёрнутое на четыре строки, читается с одного взгляда; то же условие одной строкой — с трёх перечитываний. Правило мягкое, не догма: логарифм от сложности, а не запрет.
| Было | Стало | Что изменилось |
|---|---|---|
| int x = 500; | const unsigned long PERIOD_MS = 500; | константа заглавными, единицы в имени, тип с запасом под millis() |
| if(v>400){d=1;}else{d=0;} | if (uroven > POROG_NIGHT) { rejim = REJIM_NOC; } | пробелы вокруг операторов, скобки на своих строках, имя вместо шифра |
| отступ: таб, потом 3 пробела, потом таб | одна глубина отступа на весь файл | смешение красит git-разницу целиком и путает чужой редактор |
| digitalWrite(13, HIGH); | digitalWrite(PIN_LED, HIGH); | пин 13 без контекста: что там висит — лампа, бортовой диод, датчик? |
| // увеличить задержку | // 500 мс: глаз читает ритм, быстрее уже мерцание | комментарий отвечает на «почему», с числом и условием |
Комментарии: «почему», а не «что»
Комментарий «что» пересказывает код и умирает при первой правке: строка «установить пин в HIGH» над digitalWrite(PIN_LED, HIGH) не добавляет ни бита знания. Комментарий «почему» хранит то, чего в коде нет и быть не может: 500 мс — потому что глаз читает ритм, а быстрее уже мерцание; замер делаю дважды — первый после включения датчика мусорный; пин не 13, потому что бортовой диод путает ночное свечение. Такие строки через полгода стоят дороже любой внешней документации, потому что лежат ровно там, куда упал взгляд.
Тест на полезность двухшаговый: закройте код и попробуйте по одним комментариям восстановить замысел. Получилось — комментарии на месте. Не получилось и в файле одни «цикл по массиву» с «концом функции» — стирайте без сожалений: имена уже говорят то же самое, только не устаревают. Комментарий, который врёт, хуже отсутствующего: он требует сначала доверия, потом разоблачения, потом нервов. Хуже всех сторож от удалённого кода: он охраняет то, чего больше нет, и через месяц кто-нибудь вернёт кусок «как было».
Порядок, который я применяю к любому чужому или собственному месячной давности коду: сначала Ctrl+T, чтобы формат перестал шуметь; затем переименование трёх-пяти худших имён; затем комментарий с причиной у каждого числа, которое без причины. Только после этого файл готов к правкам по существу — до того момента любая правка влезает в хаос, где половина внимания уходит на расшифровку строк.
Инструмент для переименования в IDE — поиск и замена по Ctrl+F, с одной оговоркой: короткие имена меняются опасно. Замена «d» на «svetGorit» заденет половину файла, включая те места, где d означало что-то другое, — поэтому сначала имя делают уникальнее, потом меняют. Автоформат от такой правки не пострадает, а глаза скажут спасибо уже на следующем чтении.
// Маяк на два режима: медленный (свет из окна) и быстрый (поиск).// Кнопка D2 переключает темп, светодиод D12 мигает по millis(). const int PIN_LED = 12;const int PIN_BTN = 2; const unsigned long MEDLENNO_MS = 900; // 0,5 Гц: ритм видно через комнатуconst unsigned long BYSTRO_MS = 120; // 8 Гц: выше глаз сольёт вспышки unsigned long momentTikta = 0;unsigned long momentFronta = 0;unsigned long period = MEDLENNO_MS;bool svetGorit = false;bool prevBtn = HIGH; // INPUT_PULLUP: нажато = LOW void setup() { pinMode(PIN_LED, OUTPUT); pinMode(PIN_BTN, INPUT_PULLUP); Serial.begin(9600); Serial.println("Mayak: knopka = smena rejima");} void loop() { bool btn = (digitalRead(PIN_BTN) == LOW); if (btn && !prevBtn && millis() - momentFronta > 50) { // 50 мс гасят дребезг momentFronta = millis(); if (period == MEDLENNO_MS) { period = BYSTRO_MS; } else { period = MEDLENNO_MS; } Serial.print("rejim="); Serial.println(period); } prevBtn = btn; if (millis() - momentTikta >= period) { momentTikta = millis(); svetGorit = !svetGorit; digitalWrite(PIN_LED, svetGorit); }}Маяк выше прогнан через Ctrl+T и переименован по правилам: PERIOD вместо t3, svetGorit вместо d, у каждой константы число с причиной. Скелет тот же, что у мигалки из урока про первый скетч — поведение не изменилось ни на миллисекунду, зато каждое имя отвечает «что это» ещё до чтения логики. Выгода всплывает в неподходящий момент: когда скетч не собирается, ровный файл с номерами строк читается вместе с логом компиляции за минуту — методика разбора собранных записей ждёт в уроке про чтение лога. А если план в том, чтобы получить помощь на форуме, отформатированный скетч с говорящими именами повышает шансы на ответ раньше, чем вежливость. Следующий уровень того же процесса — сворачивать повторы в функции и выносить их в библиотеку, как в уроке про библиотеки в IDE; форматирование — фундамент, на котором это строится.
- Ctrl+T (Cmd+T на macOS) выравнивает отступы и пробелы по единому набору правил; движок — clang-format, результат воспроизводим у всех
- Автоформат не переименовывает переменные и не чинит логику: имена, константы и комментарии остаются работой автора
- Имена в camelCase со смыслом и единицами (PERIOD_MS, delayUs), константы заглавными — так написан и весь официальный API ядра
- Табы или пробелы — что-то одно на весь файл; смешение красит git-разницу целиком и путает историю правок
- Комментарий отвечает на «почему», с числом и условием: «500 мс — глаз читает ритм», а не «устанавливаем пин в HIGH»
- 1. Что именно делает команда Auto Format по Ctrl+T?
- 2. Какая форма имён соответствует официальному API ядра Arduino?
- 3. Чем опасно смешение табов и пробелов в одном файле?
- 4. Какой комментарий будет полезен через полгода?
- 5. В условии стоит магическое число: if (t > 400). Первое действие?
Частые вопросы по теме урока
- Что именно делает команда Auto Format по Ctrl+T?
- Сфера действия — пробелы, отступы, переносы; движок IDE 2 — clang-format. Имена, логика и комментарии остаются на совести автора. выравнивает отступы, пробелы и переносы по фиксированным правилам
- Какая форма имён соответствует официальному API ядра Arduino?
- Весь официальный API — camelCase: digitalWrite, analogRead, attachInterrupt. Заглавными в Arduino-коде пишут константы, а не функции. camelCase: digitalWrite
- Чем опасно смешение табов и пробелов в одном файле?
- Компилятору всё равно, а истории правок нет: разница красится целиком, авторство строк теряется. Выберите один вариант и не меняйте его посреди файла. git показывает сдвиги отступа как правки, и разница тонет в шуме
- Какой комментарий будет полезен через полгода?
- Полезный комментарий хранит то, чего в коде нет: причину с числом и условием. Остальные пересказывают код и умирают при первой правке. // 500 мс: глаз читает ритм, быстрее уже мерцание
- В условии стоит магическое число: if (t > 400). Первое действие?
- Магическое число в условии нечитаемо через полгода: 400 чего, откуда. Константа заглавными с комментарием-причиной отвечает без вас. вынести в константу POROG_MS с комментарием, почему именно 400