Учитесь на чужих ошибках

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

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

Запуск в потоке пользовательского интерфейса

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

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

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

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

Обратный ад

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

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

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

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

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

Неэффективные SQL-запросы

Многие решения используют базы данных. Почти каждое веб-приложение собирает пользовательские данные и должно их где-то хранить. Мобильные приложения также организуют структурированные данные в базы данных. И разработчики с удовольствием их применяют. SQL-запросы легко читаются. Более того, объектно-реляционное отображение (ORM) позволяет составлять запросы в виде кода.

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

Например, одна ошибка, которую я время от времени совершаю, — это «запросы N+1». Он может появиться при выполнении запроса к таблице со связями с другими таблицами. Ленивая загрузка имеет здесь негативное влияние. Потому что для каждой записи в результате будет выполняться еще один дополнительный запрос.

Чтобы лучше понять проблему, давайте представим, что в нашей базе данных есть две таблицы: Employee и Company. Они имеют отношение один ко многим, поскольку сотрудник может работать только в одной компании, а компании могут иметь много сотрудников. Следующий SQL-запрос может вызвать эту проблему в платформах с отложенной загрузкой данных.

SELECT * FROM Company WHERE Employee.name = "James"

Если запрос вернет 200 компаний, в которых есть сотрудник с именем Джеймс, то всего будет запущено еще 200 дополнительных запросов. Каждый из них загружает информацию о каждом сотруднике. Итого получаем 201 запрос.

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

Выводы

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

- Альберт Эйнштейн

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