Pull to refresh

Неразрешимые проблемы программирования

Level of difficultyEasy
Reading time8 min
Views12K

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

Я с удивлением обнаружил, что вещи, казавшиеся важными, с высоты опыта оказались второстепенными. А незаметные — превратились в огромные проблемы.

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

Даже сама наша дисциплина выросла из неразрешимой задачи — Давид Гильберт назвал её Entscheidungsproblem — и, если перевести название на русский, получится смешной каламбур.

Неразрешимая проблема разрешения.

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

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

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

Я не отрицаю, что академические знание о мире важны и нужны. Полезно опознавать NP-полные задачи, полезно знать задачи неразрешимые, и в целом, полезно понимать причины, а не зубрить правила.

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

Собеседования

Мы сейчас ограничимся программистской спецификой. Понятно, что у рекрутеров, тимлидов и HR-менеджеров свои трудности с соискателями. Вольётся ли человек в коллектив? Это не наша забота. Наша забота — за час времени определить технический уровень кандидата.

Сейчас общепризнанно, что нестандартные задачки для этого не сильно подходят: когда-то их давали в Microsoft, но потом перестали. Иногда их дают в других компаниях. Мы научились более-менее отвечать на вопросы, почему люки круглые, или сколько заправок в Нью-Йорке, но, похоже, они не помогают работодателю оценить программиста.

Потому что на работе — обычно — нам не приходится решать нестандартные задачи. Нам не приходится решать олимпиадные задачи. И что мы оцениваем, задавая вопрос про люки — не известно никому.

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

Когда я проводил собеседования, я собирал вопросы, то останавливался на тех, где сообразительность не требовалась и алгоритм был очевидным. Основная трудность тех вопросов была в том, чтобы заметить особые случаи и граничные условия.

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

Честно, я не знаю. У меня не было шанса проверить свои гипотезы про правильные вопросы.

Единственная информация, которую я могу назвать объективной, найдена мною в книге Даниэля Канемана “Думай медленно… решай быстро”.

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

Что же это за процедура? На самом деле, неважно. В книге написано:

Хорошо предсказывают значимые результаты простые равновесные формулы, основанные на существующей статистике или здравом смысле. Алгоритм, сочинённый “на коленке“, по результативности часто соперничает с оптимально взвешенной формулой и с легкостью превосходит прогноз эксперта.

Канеман утверждает, что любая разумная формула работает лучше интуиции. Прочитав об этом, я стал задавать соискателям вопросы и записывать количество правильных ответов. Всё ещё не доверяя подобному “примитивному” подходу, я ранжировал вопросы по сложности. Это было несколько лет назад.

Сейчас я думаю, что вопросы должны быть простыми, чтобы на них можно было ответить “да”, “нет”, “пять” или “никогда”. Посмотри на код, и скажи, что он напечатает. Напиши функцию, которая заполнит массив числами 1 и 3 по-очереди. Что-то в этом роде.

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

Ошибиться может любой. Чтобы нивелировать эффект случайных ошибок, вопросов должно быть много. Здесь срабатывает закон больших чисел. По закону больших чисел, чем больше испытаний мы провели, тем ближе наши практические результаты к теоретическим. Если мы бросили монету два раза, то с большой вероятностью можем получить “все орлы” или “все решки”. А двадцать раз, то вероятность “всех орлов” или “всех решек” одна миллионная.

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

Хороший ли это способ? Я не знаю. Канеман проводил исследования и, если верить ему, это самый работающий способ. Большинству моих коллег он не нравится: они считают, что интуиция работает лучше каких-то там формул. В целом, в индустрии всё ещё нет общепризнанного мнения, как проводить собеседования.

Онбординг

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

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

Что такое этот проект? Количество файлов (модулей) — от четырёх до восьми тысяч. Размер отдельных модулей — до пяти тысяч строк. Размер отдельных методов — до четырёхсот строк, кодогенерацию в расчёт не берём. Даже чтобы прочитать весь код, нужно полгода. А его надо не просто прочитать, но и понять. Сколько времени занимает такой — беспощадный и сермяжный — онбординг?

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

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

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

Комментарии устаревают, комментарии лгут. Комментарии написаны людьми, которые не умеют писать понятно. Комментарии не помогают разобраться, как работает программа. Вам всё равно придётся читать код и сидеть в отладчике.

Бывают ли хорошие комментарии? Возможно. Надо ли их писать? Наверное. Как? Пишите хорошие комментарии и не пишите плохие. Совет выглядит дурацким как раз потому, что он дурацкий и не прячется за сложными формулировками. Этому дурацкому совету не последует никто, поэтому я не боюсь его давать. Я не знаю, как писать хорошие комментарии. Любого, кто знает, вы можете проверить — возьмите его код и отдайте новичку. Если новичок будет ругаться, считайте, что вас обманывают.
А сразу за комментариями в списке волшебных средств следует документация.

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

А они ведь деньги на этом зарабатывают. Если же речь идёт о легаси-проекте, то там качество документации такое же, как и у комментариев. Она неполная, она противоречивая, она не соответствует коду. Удачного онбординга!

Тесты. В идеале тесты — это короткие функции, которые рассказывают нам о коде. Что вернёт binary_search, если элемент в массиве не найден? Смотрим в тест, и он подсказывает, что результатом будет минус один. На практике, модульных тестов довольно мало. Там, где они есть, они большие, на тридцать или сорок строк. И они очень неустойчивы. После каждой правки падает десяток тестов, и вам трудно разобраться, почему.

Ещё у тестов высокий порог входа. Выучить по книге C++ проще, чем научиться писать модульные тесты, тем более перед кодом. Не верите? Спросите у знакомых программистов. Тех, кто знает C++ и не ходил при этом на курсы — довольно много. А вот тех, кто реально практикует TDD — проценты. Буквально. Два-три человека на сто знакомых программистов.

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

Код-ревью

Код-ревью — моя любимая тема. Здесь прекрасно всё, даже трудно выбрать, с чего начать. Да, вы и сами всё знаете.

Начнём с главного-очевидного. Почему-то так вышло, что среди программистов много душнил. Мы любим спорить, любим сомневаться даже в очевидном, плюём на авторитеты. И при этом, высказывая своё — безусловно ценное — мнение, редко заботимся о тонкой душевной организации коллег.

Когда нас называют идиотами, мы конечно, возмущаемся. А когда мы называем идиотами — нет.

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

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

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

Третья мысль: существующие средства код-ревью — страница Pull Request или Merge Request — не очень подходят для вдумчивого изучения исходников. Бывает и такое, что изменения влияют на код в другом модуле, на который мы даже и не смотрим.

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

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

Поэтому я делаю код-ревью в режиме “кажется, здесь нет никакой дичи”. Возможно, этого достаточно.

Заключение

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

Или, возможно, вы знаете другие неразрешимые задачи (ну, помимо проблемы останова, конечно!) Или у вас есть поучительные истории из жизни. Такие истории гораздо интереснее, чем положительный опыт, так что тем более пишите!

Tags:
Hubs:
+37
Comments28

Articles