Я занимаюсь программированием уже более 30 лет, начиная с машин, уже устаревших (на процессорах Z80 и 6502) до современных, используя языки BASIC, ассемблера, C, C++, Tcl, Perl, Lisp, ML, occam, arc, Ruby, Go и многие другие.
Далее следует список того, чему я научился.
0. Програмиирование - это ремесло, а не наука или инженерия
Программирование гораздо ближе к ремеслу, чем к науке или инженерной дисциплине. Это комбинация умения и опыта, выраженная с помощью инструментов. Разработчик выбирает нужные инструменты (иногда создает собственные) и учится использовать их в процессе создания.
Мне кажется, что это ремесло. Я думаю, что лучшие программисты похожи, скорее, на часовщиков, чем на строителей или физиков. Конечно, программирование похоже и на науку и на инженерную дисциплину из-за использования логики и математики, но в основе это использование инструментов и создание чего-то с их помощью.
Принимая во внимание, что это ремесло, нетрудно заметить, что важны опыт, инструменты и интуиция.
1. Честность - главное правило
В процессе написания кода иногда возникает соблазн поэкспериментировать, чтобы узнать, будет ли это работать и запустить программу, не понимая, что же, собственно, происходит. Классический пример - вызов API, который вы решаете оставить потому, что, волшебным образом, он позволяет избавиться от бага или добавление printf, в результате которого программа перестает падать.
Оба примера - примеры нечестности. Спросите себя: «Понимаю ли я, почему моя программа делает X?». Если не понимаете, позже у вас возникнут проблемы. Знать, что происходит - ответственность программиста, потому что компьютер выполняет точно то, что ему приказывают, а не то, что ему бы хотелось.
Честность требует строгого отношения. Вы должны быть точно уверены в том, что знаете, что ваша программа делает и почему.
2. Упрощайте, упрощайте, упрощайте
Тони Хоар сказал: «Есть два способа спроектировать систему. Один - сделать ее настолько простой, что в ней очевидно не будет недостатков, а второй - сделать ее настолько сложной, что в ней не будет очевидных недостатков. Первый способ намного более труден.»
Упрощайте, переписывайте, удаляйте.
Перефразируя Хоара скажу: «Внутри каждой большой и сложной программы есть маленькая и элегантная программа, которая делает то же, что и большая и делает это правильно.»
К этому имеет отношение подход «небольшие элементы, нежестко связанные друг с другом». Лучше проектировать программу, состоящую из небольших частей, взаимодействующих друг с другом, чем создавать огромное монолитное целое. Вот почему UNIX оказалась успешной.
3. Отладчики иногда вредят, профилировщики - никогда
Я почти никогда не пользуюсь отладчиками. Мои программы создают журналы и я знаю, что мои программы делают. Обычно, я могу понять, что не так с моим кодом из журнала, не обращаясь к помощи отладчика.
Причина, по которой я не пользуюсь отладчиками - моя уверенность в том, что отладчики заставляют лениться. Обнаружив ошибку, многие разработчики запускают отладчик, выставляют точки останова и исследуют память или значения переменных. Легко поддаться очарованию от такого мощного инструмента, но недостаточные размышления приведут к большей потере времени. А если ваша программа такая сложная, что отладчик вам необходим, см. п.2
(Замечание: несмотря на все вышесказанное, один из уважаемых мной программистов, Джон Остерхут, похоже, целыми днями сидит в отладчике Windows).
С другой стороны, профилировщики необходимы, когда вам нужно разобраться с производительностью. Вы никогда не перестанете удивляться тому, что может поведать вам профилировщик.
4. Дублированный код подставит вам ножку
Не повторяйтесь. Делайте все однократно в вашем коде.
Этот урок имеет отношение к п.2, но это особый случай. Даже простой кусочек дублированного кода причинит неприятности позже, когда вы «пофиксите» его в одном месте, но забудете в другом.
5. Не привыкайте к языкам программирования
Некоторые люди одержимы конкретными языками программирования и вынуждены пользоваться только ими. Это ошибка. Не существует языка, подходящего для решения всех задач.
Важно знать, какой язык из вашего инструментария вы будете использовать для конкретной проблемы. И лучше иметь побольше инструментов. Пробуйте разные языки, применяйте их на практике.
Например, вам не понадобится Python или ML, но вы можете поэкспериментировать со списочными выражениями и увидеть их силу. Или «поиграйте» с Go и увидите, как он работает с многопоточностью. Или используйте Perl и увидите как гибко он работает со строками. Или используйте PHP, чтобы быстро создать динамическую веб-страницу.
Я не люблю языковые войны. Они - удел неудачников, потому что они спорят о неверных вещах. Для меня PHP неуправляемый инструмент, а в руках других он творит чудеса. То же можно сказать о C++.
6. Проще вырастить программу, чем построить ее
Это правило имеет отношение к п.2. Начните с малого и растите постепенно. Когда решаете проблему, легче всего начать с небольшой ее части (возможно, используя временные заглушки или эмулируя недостающие части), чем сразу придумать целую огромную систему.
Если вы сразу создаете целую систему, то: (a) вы не понимаете ее, (b) придумываете лабиринт, который очень трудно изменить. С другой стороны, если у вас много небольших элементов, взаимодействующих друг с другом, проведение рефакторинга будет легче, если вы поймете, что ошиблись где-то в начале.
Причина в том, что вы никогда не узнаете какой должна быть действительно правильная архитектура. Очень редко можно предугадать, какими будут внешние воздействия на вашу программу. Вы можете быть уверены в том, что знаете, как проходит TCP трафик через ваш почтовый сервер или количество адресатов, а может, вы никогда не слышали о спаме. Всегда найдется что-то, что нарушит ваши предположения, а если они образуют огромную, запутанную, сложную программу, это значит - у вас серьезные неприятности.
7. Изучите уровни приложения
Я думаю, что понимание того, что происходит в программе, начиная с центрального процессора и заканчивая языком, который вы используете, очень важно. Важно понимать уровни приложения (например, понимать, что происходит со скомпилированным кодом C или как работает виртуальная машина Java).
Это очень помогает, когда приходится решать проблемы производительности, а также при отладке. Я помню случай, когда один из клиентов моей компании прислал скриншот ошибки в Windows 2000, на котором были видны состояние маленького участка памяти и регистров. Используя только эту информацию и зная версию программы, мы смогли обнаружить проблему с нулевым указателем и ее причину.
8. Я недостаточно молод, чтобы знать все
Мне все еще нужно многому научиться. Есть языки, с которыми я почти не работал, но хотел бы (Erlang, Clojure). Есть языки, с которыми я «поиграл», но не знаю их достаточно хорошо (JavaScript), а еще есть идеи, которые я почти не понимаю (монады).
PS: мне указали на то, что я не упомянул о тестировании. Стоило бы добавить, я уверен в том, что тестирование важно для любого кода, который будет использоваться достаточно долго. Возможно, если я буду заниматься программированием еще 30 лет, у меня будет ответ на вопрос «улучшают ли юнит-тесты качество программ?». Я писал код и с тестами и без тестов и до сих пор не уверен, что знаю точный ответ, хотя и склоняюсь к тому, что юнит-тесты могут иметь положительный эффект.
Оригинальная статья Джона Грэхем-Камминга http://blog.jgc.org/2012/07/some-things-ive-learnt-about.html