«Прибить гвоздями» и «захардкодить» — жаргонные термины, означающие жёсткую фиксацию значений прямо в коде программы, вместо того чтобы выносить их в настройки или конфигурацию. Hardcode — один из самых известных антипаттернов в разработке, поскольку он снижает гибкость и переиспользуемость кода. По данным Refactoring Guru, хардкод усложняет тестирование, поддержку и адаптацию приложения под разные окружения. Осознанное использование констант вместо хардкода — признак зрелой архитектуры.
Главное
Захардкодить (прибить гвоздями) — встроить конкретное значение в код программы так, что для его изменения потребуется редактировать исходный код и перекомпилировать приложение. Метафора «прибить гвоздями» точно отражает суть: значение зафиксировано намертво, и оторвать его от кода можно только с усилием.
Пример хардкода — URL сервера, записанный строкой прямо в теле функции. Если сервер переезжает на другой адрес, разработчику нужно найти строку в коде, изменить её, заново собрать приложение и выкатить релиз. В приложении с правильной архитектурой такой URL был бы вынесен в конфигурационный файл, переменную окружения или сервис конфигурации.
Термин «прибить гвоздями» более эмоционально окрашен: он подчёркивает, что значение вставлено намертво и без возможности быстрой замены. В русскоязычной среде оба выражения используются как полные синонимы с негативной коннотацией. Иногда хардкод иронично называют «константой, вынесенной в отдельную константу из константы».
Хардкод — антипаттерн, потому что он нарушает принципы поддерживаемости, тестируемости и расширяемости кода. В коде, где значения «прибиты гвоздями», любое изменение окружения, дизайна или логики требует ручного поиска и замены в исходниках. Это увеличивает риск ошибок и замедляет разработку.
Рассмотрим конкретные последствия хардкода на примере типичного мобильного приложения. Если размер отступа у всех кнопок задан числом в коде, а не через ресурс — изменение дизайна потребует поиска всех вхождений и замены. Если URL эндпоинта жёстко прописан — переключение между средами (dev, stage, prod) невозможно без пересборки.
| Последствие | Описание | Уровень критичности |
|---|---|---|
| Сложность поддержки | Изменение требует поиска по всему коду | Высокий |
| Ошибки при копировании | Не все вхождения находят и заменяют | Высокий |
| Невозможность тестирования | Нельзя подставить тестовые данные | Средний |
| Проблемы с локализацией | Тексты в коде не переводятся | Средний |
| Усложнение code-review | Проверяющий должен помнить все контексты | Низкий |
Функция, которая использует магические числа и жёстко прописанные строки, — классика хардкода. Через месяц автор не вспомнит, что означает 18, 0.07 и 2.5. Через год — никто в команде не решится менять эти числа, опасаясь сломать логику. Вынос значений в именованные константы делает код самодокументируемым.
// Bad: magic numbers and strings
fun calculatePrice(base: Double): Double {
val tax = base * 0.07
val tip = base * 0.15
val discount = if (base > 100) 10 else 0
return base + tax + tip - discount
}
Захардкоженный URL базы данных не позволит запускать тесты на локальной in-memory БД. Разработчику придётся поднимать полноценный сервер или править код перед тестированием. Вынос конфигурации из кода решает проблему: тесты используют тестовые параметры, production — боевые, и код при этом не меняется.
Хардкод — антипаттерн, но существуют легитимные исключения, когда жёстко прописанное значение не только допустимо, но и предпочтительно. Граница проходит по оси изменяемости: если значение никогда или почти никогда не меняется в рамках жизненного цикла приложения, его можно захардкодить. Если хотя бы потенциально может измениться — выносите в конфигурацию.
Математические и физические константы — числа Пи, ускорение свободного падения, количество миллисекунд в секунде — безопасны для хардкода. Они определены природой или стандартами и не изменятся. Размеры массивов-констант, определённые спецификацией, тоже можно жёстко зафиксировать, но с комментарием о происхождении числа.
Количество миллисекунд в секунде — стабильная константа, определённая стандартом времени. Нет смысла выносить её в конфиг, потому что она никогда не изменится. Однако даже такие константы лучше объявлять с понятным именем, чтобы код не содержал «магических чисел»: вместо 1000 писать MILLISECONDS_IN_SECOND.
// Justified hardcode: stable constants
private const val MILLIS_IN_SECOND = 1000
private const val LOGIN_TIMEOUT_SECONDS = 30
fun formatDuration(ms: Long): String {
val seconds = ms / MILLIS_IN_SECOND
return "${seconds} sec."
}
Существует несколько проверенных способов избежать хардкода, каждый из которых подходит для своего типа значений. Выбор альтернативы зависит от того, насколько часто меняется значение и кто его меняет: разработчик, devops или конечный пользователь.
Для URL серверов, ключей API и feature-флагов используйте конфигурационные файлы в форматах JSON, YAML или TOML. На Android это build.gradle с buildConfigField или res/values/config.xml. На iOS — Info.plist или xcconfig. Конфиги собираются вместе с приложением, но могут быть разными для разных схем сборки.
Для секретов (токены, пароли) и параметров окружения используйте переменные окружения. Они не попадают в репозиторий и могут различаться на dev, stage и prod серверах. В мобильной разработке переменные окружения часто эмулируют через схемы сборки Xcode или build flavors в Gradle.
Строки, цвета, размеры, изображения должны быть вынесены в ресурсные файлы: strings.xml на Android, Localizable.strings на iOS, ARB-файлы во Flutter. Это упрощает локализацию, адаптацию под разные экраны и тёмную тему. Изменение строки в ресурсах не требует переписывания кода.
<!-- Android: res/values/strings.xml -->
<resources>
<string name="app_name">MyApp</string>
<string name="api_base_url">https://api.example.com</string>
</resources>
Для сервисов и провайдеров используйте Dependency Injection через Dagger, Hilt или Koin на Android, Swinject на iOS. DI фреймворки позволяют подменять реализации на лету — для тестов, для разных сред, для разных пользователей. Это высший уровень абстракции, где «прибитость» значения заменяется внедрением извне.
Рефакторинг хардкода — процесс вынесения жёстко прописанных значений в конфигурацию или ресурсы. Это одна из самых безопасных операций рефакторинга, если выполнять её методично. Описанная ниже последовательность подходит для любого языка и платформы.
Поиск можно выполнить через IDE (Search in Project) или скриптом. Ищи строки, URL, числовые литералы, размеры, таймауты. Особое внимание — повторяющимся значениям: если одно и то же число встречается в пяти местах, это кандидат на вынос в константу. Используй grep или встроенный поиск IDEA / Xcode.
Для каждого найденного значения создай константу с осмысленным именем. Группируй константы по модулям или классам. Имя должно объяснять, что означает значение, а не как оно используется: API_TIMEOUT, а не TIMEOUT_30. После замены ни одно число в коде не должно остаться без пояснения.
// Before: magic number 0.4
let cardHeight = screenHeight * 0.4
// After: named constant
private let cardHeightRatio: CGFloat = 0.4
let cardHeight = screenHeight * cardHeightRatio
Если значение может меняться между сборками или окружениями — вынеси его в конфигурационный файл или ресурсы приложения. Для строк используй файлы локализации. Для URL — build config или xcconfig. Для размеров — файлы ресурсов (dimens.xml на Android). Проверь, что приложение собирается и работает корректно после выноса.
После рефакторинга напиши тест, который проверяет, что конфигурация загружается правильно и значения соответствуют ожидаемым. Если в будущем кто-то изменит конфиг, тест укажет на несоответствие. Тест на конфигурацию — быстрый и надёжный способ предотвратить regression.
После выноса в конфиг проверь, что все места, где использовалось старое значение, ссылаются на единый источник. Удали закомментированный код и старые константы, которые больше не используются. Финализируй рефакторинг коммитом с сообщением, описывающим, какие значения и куда были вынесены.
Часто задаваемые вопросы
Захардкодить — жёстко прописать значение в исходном коде вместо вынесения его в конфигурацию или ресурсы. Это делает код менее гибким и более сложным в поддержке.
Хардкод усложняет изменение поведения приложения, мешает тестированию, создаёт дублирование и увеличивает риск ошибок при копировании. Изменение захардкоженного значения требует пересборки и перевыпуска приложения.
Допустим для математических констант, стабильных значений, которые не меняются в жизненном цикле приложения, и для временных прототипов. В production даже константы стоит выносить в именованные переменные.
Найдите все магические числа через поиск, замените их на именованные константы или вынесите в конфигурационный файл. Напишите тест, проверяющий загрузку конфигурации. Удалите дубликаты и сделайте коммит с описанием изменений.
Константа — именованное значение в коде, доступное для изменения в одном месте. Хардкод — неименованные значения, разбросанные по коду. Хорошая практика: всегда использовать именованные константы с осмысленными названиями.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также