Наиболее важные моменты при написании кода

1. Комментарии

Для каждого выполненного задания должно быть пояснение, о чём вообще это задание. В случае с простыми задачами, которые решаются несколькими строчками кода в Main'е это должны быть комментарии к методу Main. В случае, когда вы пишете свои методы, их лучше снабжать документирующими комментариями (хотя в идеале, конечно, то, что делает ваш код, должно быть понятно и без комментариев).

2. Понятный код

К слову о понятном коде. Одной из главных составляющих читаемого кода является способ именования переменных. Переменные x, y, pr, obj1, obj2, obj3, конечно, облегчают вам жизнь и освобождают вас от нажатия на пару лишних клавиш, но при большом количестве таких переменных чтение кода превращается в игру "запомни что означает переменная xx4  и попытайся соотнести с десятью другими такими же названиями". Такой код трудно не только читать, но и дебажить (вспомните, бывали ли у вас ошибки из-за того, что вы запутались, какая переменная за что отвечает?). 

Чтобы избежать этого хаоса, нужно помнить простую идею: «Имена должны передавать намерения программиста» (эта концепция отлично описана в книге Роберта Мартина «Чистый код: создание, анализ и рефакторинг»). При чтении любого названия переменной/метода/класса/etc должно появляться чёткое представление, что эта сущность делает и для чего она используется. К примеру, рассмотрим два названия некоторого параметра функции:  exp и expirationDate. Какое из них даёт больше информации о том, что мы передаём, как параметр? Кажется, ответ очевиден. Да, выбор хорошего имени потребует некоторых усилий, но при повторном чтении вашего кода вам не придётся распутывать клубок односимвольных названий. 

Во всём, конечно, нужно знать меру. Параметры циклов, например, не требуют длинных названий, потому что любой программист поймёт, что переменная i в цикле является итерируемой. Координаты x, y, z и т. д. так же не требуют длинных описаний. Такие примеры встречаются повсеместно, главное — найти баланс. 

Ещё одна важная ошибка, которая характерна для всех программистов из не-англоговорящих стран. Названия переменных должны быть на английском, а не на транслите. То есть, названия strokaMatritsy и dataVipuska - очень плохое решение. Если уровень английского не позволяет сходу подобрать название, воспользуйтесь переводчиком. Почему так? Как минимум, вы никогда не знаете, кто будет читать ваш код. Если вам в данный момент кристально понятно, что означают ваши peremennyie, то условному Джону, которому ваш код достался в качестве легаси, придётся несладко. Кроме того, чтение таких названий вызывает сложности даже для русскоговорящих программистов (подробнее об этом можно прочитать в статье). 

3. Опрятный код

Форматирование кода чем-то напоминает форматирование книги.  Один и тот же текст может восприниматься совершенно по-разному в зависимости от его внешнего вида. Представьте, что будет, если из страницы, которую вы сейчас читаете, убрать все заголовки, списки, где-то убрать абзацы, а где-то наоборот, добавить. Получится нечто странное, и, вполне возможно, вы не сможете дочитать даже до половины. С кодом ситуация аналогичная. Принцип «главное написать код, а как он выглядит — всё равно», может быть, работал на первых порах изучения программирования, но сейчас вы должны осознавать, что ваш код будут читать другие люди, и, помимо правильных имён, текст программы требует правильного оформления. В это понятие входят отступы, количество операторов на строке, пробелы до/после скобок и знаков операций и многое другое. К счастью, мы живём в мире, где с большей частью работы по форматированию справляется IDE. В случае написания кода на C# вам помогут следующие сочетания клавиш:

    • Visual Studio —  Ctrl + K + D

    • VS code —  Shift + Alt + F

    • Rider —  Ctrl + Alt + L

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

  • ввод исходных данных;
  • вычисление результата;
  • вывод результата.

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


4. Тестирование

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

Многие студенты склонны недооценивать важность умения найти в своей программе ошибку, предпочитая подогнать своё решение под хорошие данные. Работает школьный принцип: «Учитель умный — пусть проверяет». В реальной жизни этот принцип не работает, что может привести к печальным последствиям.

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

  • нулевые и отрицательные значения,

  • для задач с вычислением логических выражений должны рассматриваться все сочетания True/False,

  • для задач, где есть условные операторы, следует проверять все ветви выполнения,

  • для задач с циклами следует проверять случаи, когда выполняется нуль и один шаг цикла,

  • для задач с контейнерами (массивами, строками и т. п.) следует проверять случай пустого контейнера; в задачах поиска — случаи, когда находится и не находится нужное; в задачах замены — случаи, когда заменяется 0, 1 и 2 вхождения (последнее — если требуется заменить больше одного).

Эти примеры, конечно, не исчерпывают все специальные случаи, которые могут встретиться в задачах: каждый раз нужно отдельно думать о подборе хороших тестовых наборов.

Итак, зафиксируем:

  • Логи необязательны, но вы должны тестировать свои программы, и чтобы показать, что вы это сделали качественно, желательно приводить логи.
  • Unit-тесты для подпрограмм так же, как и логи, писать необязательно, но желательно. Но помните, написание Unit-тестов это, помимо всего прочего, очень полезный навык для вашей дальнейшей карьеры.
  • Оба этих правила, тем не менее, не работают, когда в условиях лабораторной (домашней/контрольной) работы чётко указана необходимость тестов и/или логов.
  • За плохо протестированные программы баллы будут снижаться. Чтобы их не терять, ручное тестирование и/или Unit-тесты необходимы.