5 навыков, которые нужны программисту на базовом уровне
easy

5 навыков, которые нужны программисту на базовом уровне

Помогут вырасти и в профессии, и в карьере

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

Понимание логики кода и его работы

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

Признаки того, что что-то не так:

  • Использование «кода вуду», или кода, который не связан с целью программы, но всё равно поддерживается. Например, инициализация переменных или создание данных, которые не используются, вызов функций, которые не имеют отношения к цели, и так далее.
  • Выполнение функции в коде несколько раз «на всякий случай».
  • Исправление ошибок кодом, который перезаписывает результат ошибочного кода.
  • Использование «кода йо-йо», который преобразует какое-то значение в другое, а затем обратно, например десятичную дробь в строку, а затем снова в десятичную дробь.
  • Использование «кода бульдозера», который создаёт видимость рефакторинга за счёт разбиения фрагментов на подпрограммы, но который невозможно использовать в другом контексте.

Как научиться выполнять код в голове

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

Точка останова в VS Code
Точка останова в VS Code

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

Понимание модели программирования языка

Программирование бывает разным: объектно-ориентированным, функциональным, декларативным и так далее. Каждая из этих моделей отличается от процедурного, или императивного, программирования, так же, как процедурное программирование отличается от ассемблера или программирования на основе GOTO. Многие языки следуют основной модели программирования, например объектно-ориентированному, но с улучшениями, такими как понимание списков, обобщения, утиная типизация и так далее.

Признаки того, что что-то не так:

  • Использование синтаксиса, который необходим для выхода за пределы модели, а затем написание оставшейся части программы в стиле знакомого языка.
  • Вызов нестатических функций или переменных в неэкземплярных классах и трудности с пониманием, почему они не компилируются (ООП).
  • Написание множества классов, которые содержат все методы управления полями объектов и имеют мало или вообще не имеют собственных методов (ООП).
  • Обработка реляционной базы данных как хранилища объектов и выполнение всех объединений и принудительного применения отношений в клиентском коде (реляционное программирование).
  • Создание нескольких версий одного и того же алгоритма для обработки разных типов или операторов вместо передачи функций высокого уровня в общую реализацию (функциональное программирование).
  • Ручное кеширование результатов детерминированной функции на платформах, которые делают это автоматически (функциональное программирование, например SQL и Haskell).
  • Использование фрагментов кода из чужой программы для работы с IO и монадами (функциональное программирование).
  • Установка отдельных значений в императивном коде вместо связывания данных (декларативное программирование).

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

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

Например, принципы объектно-ориентированного программирования можно понять по такой цепочке утверждений:

  1. ООП — это просто записи с методами.
  2. Методы ООП — это просто функции, которые выполняются в мини-программе с собственными глобальными переменными.
  3. Глобальные переменные — это поля, некоторые из которых приватные и не видны снаружи мини-программы.
  4. Приватные и публичные элементы используются для того, чтобы скрыть детали реализации и оставить чистый интерфейс — это называется инкапсуляцией.
  5. Инкапсуляция означает, что бизнес-логику кода не нужно засорять деталями реализации.

А принципы функционального программирования можно понять по такой цепочке:

  1. Функциональное программирование — это объединение детерминированных функций, то есть функций, которые возвращают одинаковый результат для одного и того же набора входных значений.
  2. Когда функции детерминированы, компилятор может предсказать, что можно кешировать результаты, пропустить вычисление и даже безопасно остановить его преждевременно.
  3. Для поддержки ленивой и частичной оценки функций компилятору нужно, чтобы функции были определены с точки зрения преобразования одного параметра, иногда в другую функцию, — это называется каррированием.
  4. Иногда компилятор может выполнить каррирование за разработчика.
  5. Позволив компилятору разобраться в деталях, можно создавать программы, описывая результат, который хочется получить, а не то, как его добиться.

Постоянное изучение особенностей языка или платформы

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

Признаки того, что что-то не так:

  • Незнание базовых механизмов, которые встроены в язык, таких как события, обработчики или регулярные выражения.
  • Переизобретение классов и функций, встроенных в структуру языка, например таймеров, коллекций, алгоритмов сортировки и поиска.
  • Просьбы прислать готовый код, а не подсказать вектор движения по застопорившемуся моменту.
  • Использование «кода обхода», который выполняет то, что можно сделать гораздо меньшим количеством инструкций, например округление числа путём преобразования десятичной дроби в форматированную строку с последующим преобразованием строки обратно в десятичную.
  • Регулярное использование устаревших методов, даже если новые в конкретной ситуации лучше, например именованных функций вместо лямбда-выражений.
  • Постоянное пребывание в зоне комфорта и решение сложных проблем с помощью примитивов.

Как постоянно изучать особенности языка или платформы

Чтобы не переставать учиться, придётся отвлекаться от написания кода. Поможет второй монитор: на нём можно искать ответы на возникшие вопросы, сверяться с документацией по языку или платформе. Хорошим упражнением может быть рефакторинг своего старого кода: чем сильнее получится его уменьшить, тем лучше.

Понимание указателей и ссылок

Концепция указателей позволяет создавать сложные структуры данных и эффективные API. Управляемые языки используют вместо указателей ссылки с аналогичным функционалом. Неспособность понять эту концепцию будет выливаться в плохое проектирование структуры данных и ошибки. Без понимания указателей и ссылок у программиста будет ограниченный ряд программ, которые он может написать.

Признаки того, что что-то не так:

  • Неспособность реализовать связанный список или написать код, который вставляет или удаляет узлы из связанного списка или дерева без потери данных.
  • Выделение произвольно больших массивов для коллекций переменной длины и поддержание отдельного счётчика размера коллекций вместо использования динамической структуры данных.
  • Неспособность найти или исправить ошибки, вызванные ошибочным выполнением арифметических операций с указателями.
  • Изменение разыменованных значений из указателей, передаваемых в качестве параметров функции, без ожидания изменения значений в области видимости вне функции.
  • Создание копии указателя, изменение разыменованного значения с помощью копии и вера в то, что исходный указатель по-прежнему указывает на старое значение.
  • Сортировка массива указателей путём их сравнения.

Как понять указатели и ссылки

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

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

Умение видеть рекурсию

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

Признаки того, что что-то не так:

  • Слишком сложные итеративные алгоритмы для задач, которые можно решить рекурсивно, например обход дерева файловой системы, особенно если память и производительность не в приоритете.
  • Использование рекурсивных функций, которые проверяют одно и то же базовое условие как до, так и после рекурсивного вызова.
  • Использование рекурсивных функций, которые не проверяют базовое условие.
  • Использование рекурсивных подпрограмм, которые объединяют или суммируют глобальную переменную или переносимую выходную переменную.
  • Очевидное путание того, что передавать в качестве параметра при рекурсивном вызове или рекурсивных вызовах, передающих параметр без изменений.
  • Уверенность, что количество итераций будет передано как параметр.

Как научиться видеть рекурсию

Попробуйте написать код только с одной проверкой базового условия и одним рекурсивным вызовом, который использует тот же самый немодифицированный параметр, который был передан. Запустите код — он выдаст исключение переполнения стека. Теперь передайте изменённую копию параметра в рекурсивный вызов. 

Несколько раз отредактируйте код и запустите его, переключаясь на настройку рекурсивного вызова, пока не начнёте интуитивно понимать, как функция преобразует входные данные. Не используйте более одного теста базового состояния или рекурсивного вызова, если пока не понимаете, что при этом происходит.

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

Что дальше

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

Художник:

Алексей Сухов

Корректор:

Ирина Михеева

Вёрстка:

Маша Климентьева

Соцсети:

Юлия Зубарева

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию Получите ИТ-профессию
Вам может быть интересно
easy