Что означают versionCode и versionName в Android
Короткий ответ: versionCode — целое внутреннее число, по которому Android/Google Play определяют, какая сборка новее; versionName — человекочитаемая строка, которую видят пользователи. Правильная схема нумерации и автоматизация versionCode предотвращают проблемы с апдейтами и публикацией.
Какие «версии» есть в Android и зачем их различать
- API level (версия ОС) — управляет совместимостью и указывается через minSdkVersion / targetSdkVersion. Это не версия приложения.
- versionName — свободная строка, видимая пользователю: "1.4.3", "2026.03.15", "1.0-beta".
- versionCode — целое число, которое сравнивает Google Play и система обновлений: новая сборка разрешена только если versionCode > установленного.
Важно: изменив только versionName без увеличения versionCode, вы не обновите приложение для пользователей.
Как пользоваться versionCode и versionName на практике
- Всегда увеличивайте versionCode при каждой публикации в Play Console.
- Храните единую логику формирования versionCode (скрипт/CI/файл), чтобы никто не забыл инкремент.
- Для разных каналов (prod/beta/internal) используйте либо разные applicationId для внутренних билдов, либо схему кодов с «шлюзами», чтобы не пересекаться.
Для публичных релизов назначайте понятный versionName (семантика или дата), а versionCode делайте автоматически увеличиваемым числом — это простая и надёжная комбинация.
Распространённые схемы нумерации и примеры формул
- Семантика MAJOR.MINOR.PATCH:
- Формула в code: versionCode = MAJOR10000 + MINOR100 + PATCH
- Примеры: 1.0.0 → 10000, 1.2.3 → 10203, 2.0.0 → 20000
- Удобно восстанавливать версию по коду, но заранее планируйте разрядность.
- Простая инкрементальная:
- Каждый релиз: versionCode = previous + 1
- Надёжно и просто, но по коду не видно масштаба изменений.
- Дата + build:
- versionCode = YYYYMMDD * 100 + BUILD
- Пример: 20260315, build 2 → 2026031502
- Удобно для CI, но следите за пределом int (~2_147_483_647).
Пример конфигурации Gradle (Kotlin DSL):
android {
defaultConfig {
val major = 1
val minor = 4
val patch = 3
versionName = "$major.$minor.$patch"
versionCode = major * 10000 + minor * 100 + patch
}
}
Пример с BUILD_NUMBER из CI:
val build = System.getenv("BUILD_NUMBER")?.toIntOrNull() ?: 0
versionName = "$major.$minor.$patch ($build)"
versionCode = major * 1_000_000 + minor * 10_000 + patch * 100 + build
Частые ошибки
- Забили инкремент versionCode — Play Console не примет сборку. Решение: инкремент в CI.
- Понизили схему (перешли на другую формулу) и получили меньший код — добавьте смещение (offset), чтобы новые коды были > текущих.
- Одинаковый versionName для разных билдов — запутывает поддержку. Для тестовых каналов добавляйте постфиксы: "1.0.0-beta (45)".
- Переполнение int — проверьте диапазон выбранной формулы и рассчитайте запас лет вперёд.
Рекомендации для команды
- Выберите схему заранее и зафиксируйте её в VERSIONING.md.
- Автоматизируйте generation/versionCode в CI и храните единственный источник правды.
- Привязывайте каждую публичную versionName к строке в changelog — так вы избегаете разницы поведения у пользователей с одинаковой versionName.
FAQ
- Нужно ли менять versionName при каждом багфиксе? Нет — достаточно изменять versionCode; но для публичных релизов лучше отражать патчи в versionName.
- Можно ли публиковать разные каналы с одним applicationId и перекрывающимися кодами? Нет: versionCode должен глобально расти внутри одного applicationId; для внутренних билдов часто используют отдельный applicationId suffix.
- Как быстро восстановить корректную последовательность кодов при смене схемы? Найдите максимальный опубликованный versionCode в Play Console и добавьте смещение (константу) к новой формуле, чтобы все новые коды были больше этого числа.
Не меняйте логику формирования versionCode без плана миграции: это почти всегда приводит к блокировкам при загрузке в Play Console.