Элементы стиля программирования

Многие технические книги начинаются с ужасно скучного и муторного введения. Исключений немного, и книга «Идеальный код», о которой я рассказал в прошлом посте — одна из тех, введение которой стоит прочесть.

Математические книги нужно читать имея под рукой ручку и бумагу, все остальные — имея под рукой гугл. Последний раз когда я читал введение в «Идеальный код», у меня не было под рукой гугла. Думаю, тогда я упустил из виду нечто важное.

Вот первые два абзаца:

Я начал работать программистом летом 1982 года. Через пару недель после этого один из системных администраторов дал мне почитать книги «The Elements of Programming Style», Brian W. Kernighan, P. J. Plauger, и «Algorithms + Data Structures = Programs», Niklaus Wirth.

Это стало для меня открытием. Я впервые узнал, что программы — это нечто большее, чем простые инструкции для компьютера. Они могут быть элегантными, как ближайшее окружение президента, изящными, как подвесной мост, и красноречивыми, как эссе Джорджа Оруэлла.

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

«Элементы стиля программирования» это небольшой ~150-страничный сборник примеров кода с пояснениями, почему приведенный код — говно. Книга действительно древняя, все примеры написаны на Фортране и PL/1 с обилием прелестей а'ля 20 goto 10, но внимания заслуживают несколько десятков «правил», разбросанных по всей книге. Определенно, эти правила важны как история становления философии UNIX. Очень похожие правила можно найти в книге Рэймонда «Искусство программирования для UNIX».

Ниже вы увидите большую часть этих правил и немного моих комментариев.

Перефразируя высказывание из «Элементов стиля» («Elements of Style» by Strunk & White), правила стиля программирования, как и стиля английского языка, иногда нарушают даже лучшие писатели. Впрочем, когда правило нарушается, обычно в программе находится некое компенсирующее качество, которое достигается за счет нарушения. Если вы не уверены в улучшении, вероятно, лучше всего будет следовать правилам.

  1. Говорите то, что вы имеете в виду, просто и прямо.

    Сама суть философии UNIX: «Будь проще, тупица».

  2. Пишите программы просто — не делайте их слишком хитроумными.

    Аналогично Правилу ясности: Ясность лучше заумности.

    Чаще всего этим грешат хаскеллисты и большие любители паттернов проектирования.

  3. Пользуйтесь библиотечными функциями.
  4. Избегайте промежуточных переменных.
  5. Пишите понятно — не жертвуйте ясностью ради «эффективности».
  6. Пусть машина делает грязную работу.

    Аналогично Правилу экономии: Время программиста дорого; сократите его, используя машинное время.

  7. Заменяйте повторяющиеся выражения вызовами функций.

    Лисперы добавят: заменяйте повторяющиеся управляющие конструкции макросами.

  8. Скобки исключают двусмысленность.

    Обилие скобочек в правильных местах избавит от двусмысленности, повысит читаемость и увеличит расширяемость — лисперы знают о чем я (loop & iterate).

  9. Выбирайте имена переменных так, чтобы они не приводили к путанице.
  10. Не используйте goto, если хотите сохранить программу читаемой.

    Мантра «структурного программирования».

  11. Избегайте ненужных ветвлений.
  12. Используйте хорошие средства языка и не используйте плохие.

    Над этим правилом уже 20 лет ломают голову программисты на С++.

  13. Используйте «телефонный тест» для проверки читабельной программы.

    Как в анекдоте: если Рабинович сможет правильно «напеть» вашу программу — она прошла тест.

  14. Используйте сдвиг строк (идентацию) для разделения блоков кода.

    Если пишите на питоне — можете проигнорировать это правило.

  15. Делайте программы читаемыми сверху вниз.
  16. Используйте if и else чтобы подчеркнуть необходимость выполнения только одного из двух действий.
  17. Используйте if … else if … else для ветвлений по нескольким направлениям.
  18. Пользуйтесь основными конструкциями потока управления.
  19. Если логическое выражение трудно понять — попробуйте его преобразовать.
  20. Пусть данные определяют структуру программы.

    Аналогично Правилу представления Реймонда: Храните знания в данных так, чтобы логика программы была тупой и надёжной.

  21. Сперва напишите на псевдокоде, потом переведите на машинный язык.

    Литературное программирование пошло дальше в этом подходе — сперва напишите на псевдокоде, а потом допишите машинным языком.

  22. Каждый модуль должен выполнять одну функцию, но хорошо.
  23. Не исправляйте плохую программу — перепишите её.
  24. Пишите и тестируйте программу небольшими частями.
  25. Используйте рекурсивные процедуры для рекурсивных структур данных.

    Рекурсия не должна быть затычкой в каждой бочке (как в языке Scheme), с ней нужно быть очень осторожным — по убийственной силе она близка старому доброму goto (если считаете иначе — попробуйте разобраться в системе из хотя бы трех взаиморекурсивных функций). В большинстве случаев лучше предпочесть итеративные конструкции рекурсивным.

  26. Проверяйте вводимые данные на обоснованность и правдоподобие.
  27. Убедитесь, что входные данные не приведут к вылету программы.
  28. Определяйте плохие входные данные; восстанавливайтесь, если это возможно.
  29. Делайте входные данные легкими для подготовки, а выходные — понятным.
  30. Используйте единый формат ввода.
  31. Делайте входные данные легко корректируемыми.
  32. Делайте входные данные понятными и используйте значения по-умолчанию.
  33. Инициализируйте переменные перед использованием.
  34. Тестируйте программы на граничных условиях.
  35. Убедитесь, что особые случаи действительно особые.
  36. Проверте некоторые результаты вручную.
  37. Будте осторожными в вычислениях с плавающими точками и дробями.
  38. Сделайте программу правильной перед тем как сделать её быстрой.
  39. Сделайте программу отказоустойчивой перед тем как сделать её быстрой.
  40. Сделайте программу ясной перед тем как сделать её быстрой.
  41. Оставте простые оптимизации компилятору.
  42. Не напрягайтесь с повторным использованием кода; вместо этого — реорганизуйте его.
  43. Держите программу простой чтобы сделать её быстрой.
  44. Не раздувайте код чтобы сделать его быстрым — найдите лучший алгоритм.
  45. Используйте профайлер. Измеряйте скорость перед тем как делать программу «эффективнее».
  46. Убедитесь, что комментарии и код совпадают.
  47. Не повторяйте код в комментариях — сделайте каждый комментарий значимым.
  48. Не комментируйте плохой код — перепишите его.
  49. Используйте значащие имена переменных и имена меток goto.
  50. Форматируйте программу так, чтобы помочь читателю понять её.
  51. Документируйте структуры данных.
  52. Не комментируйте сверх меры.