Теперь поговорим непосредственно про код. Многое вам даст стандарт MISRA C, однако я хотел бы также дать общие рекомендации. Всё, что здесь написано — исходит из моего опыта, на что напарывался лично я. Остальное можно прочитать и в MISRA, и у Александреску.

Комментарии

То, о чем любая хорошая книжка по основам программирования пишет в первых же главах, и все равно немногие программисты это делают. Особенно часто, к сожалению, это встречается среди эмбеддеров. Однако, вам нужно помнить несколько вещей.

Первое, «хороший код самодокументирован» — это миф. Никакой код не может ответить на вопрос, зачем он вообще нужен, где и как применяется и какие ограничения содержит. А если в библиотеке рядом лежат две его версии (например, работающие с разным форматом исходных данных) — то даже вдумчивое чтение кода может дать неполное представление о разнице между версиями. Простой текстовый блок с описанием функции сэкономит много времени и нервов. Тем не менее, понятные и самоописывающие имена переменных и функций — это хорошая штука.

Второе — помните, что вы пишете коммерческий код, который будут поддерживать ваши преемники. Вы хотите, чтобы вас заочно поливали грязью? Всегда помните про метрику WTF/минуту.

Третье — интересный метод разработки нового модуля или программы: сначала в комментарии простым текстом, русским языком описываем все, что программа должна делать. Потом каждое действие расписываем более подробно. После достижения нужного уровня описания начинаем под каждым блоком текста писать код, выполняющий этот блок. Вот что такое настоящий самодокументированный код! :)

Код должен быть понятным, а не лаконичным. Все эти экономии строк и прочая фигня будут съедены компилятором и не замечены.

Костыли / workaround

Обходитесь без костылей. Вы должны понимать, как оно работает.

Костыли оставьте программистам на java, они обрадуются. Также я довольно плохо отношусь к полному рефакторингу на любых стадиях, особенно не в начале — писать нужно сразу грамотно.

Модульность

Первый признак плохого кода — один файл main.c. Совершенно точно в вашем коде есть независимые блоки, которые можно вытащить в отдельный файл. Первое, что я складываю в stuff.c или main.h — это всевозможные процедуры инициализации периферии. Править вы их будете нечасто (если вообще будете), а вот глаз они вам намозолят, находясь в главном файле. Вообще, по мере развития программы я создаю файл hal.c (hardware abstraction layer), в который скидываю все функции работы с железом — при необходимости перехода на другое железо правки понадобятся только этому файлу. Можно создать несколько таких файлов под разное железо, и включать их директивами условной компиляции.

Константы и define

Тоже довольно распространённый совет. Никогда не создавайте в коде волшебные значения — такие как битрейт связи, адреса портов, IP и MAC-адреса и прочие параметры. Обязательно складывайте их в дефайны с описанием, откуда это значение взялось. Это сэкономит время правки кода (поправить IP-адрес в одном месте в начале файла — или бегать по всем файлам, и в итоге забыть поменять в паре мест), и облегчит понимание сути этих значений.

Однако, не используйте define там, где без него можно обойтись. Код библиотеки NanoPB — пример переизбытка дефайнов, эти парни половину всей логики сделали на макросах. Разбираться и ремонтировать эту библиотеку не слишком удобно, особенно с обрезанным препроцессором, не генерирующим .i-файл.

Сторонний код

Ни строчки кода в продакшн без понимания сути. Взяли код из книжки, а тем более скопипастили из интернета — всё в топку и переписывать. Названия книжек, адреса сайтов, великие имена великих писателей этого гениального кода — ничто не может быть оправданием перед непониманием алгоритма работы этого кода. А если там ошибка — на кого вы будете сваливать ответственность?

Я не говорю про известные, проверенные годами библиотеки — их переписывать довольно дорого и глупо. Но знать, что и как они делают — нужно обязательно. Тем более в эмбеде не нужны всякие монструозные библиотеки вроде Flash и подобных.

Инициализация переменных

Всегда инициализируйте свои переменные, даже те что лежат не в основной памяти. Комично выглядит ситуация, когда сразу после прошивки девайса и до подключения его к внешним системам он внезапно выдаёт ненулевые значения каких-то индицируемых параметров.