“Nail down” and “hardcode” are slang terms meaning the rigid fixation of values directly in the program code, instead of moving them to settings or configuration. Hardcode is one of the most well-known anti-patterns in development because it reduces code flexibility and reusability. According to Refactoring Guru, hardcode complicates testing, maintenance, and application adaptation to different environments. Conscious use of constants instead of hardcode is a sign of mature architecture.
Key Takeaways
Hardcode (nail down) — embedding a specific value into the program code so that changing it requires editing the source code and recompiling the application. The metaphor “nail down” accurately reflects the essence: the value is fixed permanently, and it can only be detached from the code with effort.
An example of hardcode is a server URL written as a string directly in a function body. If the server moves to a different address, the developer needs to find the string in the code, change it, rebuild the application, and ship a release. In an application with proper architecture, such a URL would be placed in a configuration file, environment variable, or configuration service.
The term “nail down” is more emotionally charged: it emphasizes that the value is inserted permanently with no possibility for quick replacement. In the Russian-speaking community, both expressions are used as complete synonyms with a negative connotation. Sometimes hardcode is ironically called “a constant extracted into a separate constant from a constant.”
Hardcode is an anti-pattern because it violates the principles of maintainability, testability, and extensibility. In code where values are “nailed down,” any change to the environment, design, or logic requires manual search and replacement in the source code. This increases the risk of errors and slows down development.
Let’s examine the specific consequences of hardcode using a typical mobile application as an example. If the margin for all buttons is set as a number in code rather than through a resource, changing the design requires finding all occurrences and replacing them. If the endpoint URL is hard-coded, switching between environments (dev, stage, prod) is impossible without rebuilding.
| Consequence | Description | Severity Level |
|---|---|---|
| Maintenance Difficulty | Changes require searching through the entire codebase | High |
| Copy-Paste Errors | Not all occurrences are found and replaced | High |
| Testing Limitations | Cannot substitute test data | Medium |
| Localization Issues | Texts in code cannot be translated | Medium |
| Code Review Complexity | Reviewer must remember all contexts | Low |
A function that uses magic numbers and hard-coded strings is a classic example of hardcode. After a month, the author won’t remember what 18, 0.07, and 2.5 mean. After a year, no one on the team will dare to change these numbers for fear of breaking the logic. Extracting values into named constants makes the code self-documenting.
// 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
}
A hardcoded database URL will not allow running tests on a local in-memory database. The developer will have to spin up a full server or modify the code before testing. Moving configuration out of the code solves the problem: tests use test parameters, production uses live ones, and the code remains unchanged.
Hardcode is an anti-pattern, but there are legitimate exceptions where a hard-coded value is not only acceptable but preferable. The boundary runs along the axis of changeability: if a value never or almost never changes within the application’s lifecycle, it can be hardcoded. If it could potentially change, move it to configuration.
Mathematical and physical constants — Pi, gravitational acceleration, the number of milliseconds in a second — are safe for hardcode. They are defined by nature or standards and will not change. Sizes of constant arrays defined by specification can also be hard-coded, but with a comment about the number’s origin.
The number of milliseconds in a second is a stable constant defined by the time standard. There is no point in moving it to a config because it will never change. However, even such constants are better declared with a clear name so that the code does not contain “magic numbers”: instead of 1000, write 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."
}
Several proven methods exist to avoid hardcode, each suitable for its own type of values. The choice of alternative depends on how often the value changes and who changes it: the developer, devops, or the end user.
For server URLs, API keys, and feature flags, use configuration files in JSON, YAML, or TOML formats. On Android, this includes build.gradle with buildConfigField or res/values/config.xml. On iOS, Info.plist or xcconfig. Configs are bundled with the application but can differ for different build schemes.
For secrets (tokens, passwords) and environment parameters, use environment variables. They do not end up in the repository and can differ on dev, stage, and prod servers. In mobile development, environment variables are often emulated through Xcode build schemes or Gradle build flavors.
Strings, colors, sizes, and images should be placed in resource files: strings.xml on Android, Localizable.strings on iOS, ARB files in Flutter. This simplifies localization, adaptation to different screens, and dark mode support. Changing a string in resources does not require rewriting code.
<!-- Android: res/values/strings.xml -->
<resources>
<string name="app_name">MyApp</string>
<string name="api_base_url">https://api.example.com</string>
</resources>
For services and providers, use Dependency Injection via Dagger, Hilt, or Koin on Android, Swinject on iOS. DI frameworks allow swapping implementations on the fly — for tests, different environments, or different users. This is the highest level of abstraction, where “nailing down” a value is replaced by external injection.
Refactoring hardcode is the process of extracting hard-coded values into configuration or resources. It is one of the safest refactoring operations when done methodically. The sequence below works for any language and platform.
Search can be done through the IDE (Search in Project) or with a script. Look for strings, URLs, numeric literals, sizes, and timeouts. Pay special attention to duplicate values: if the same number appears in five places, it is a candidate for extraction into a constant. Use grep or the built-in search of IDEA / Xcode.
For each found value, create a constant with a meaningful name. Group constants by modules or classes. The name should explain what the value means, not how it is used: API_TIMEOUT, not TIMEOUT_30. After replacement, no number in the code should remain without explanation.
// Before: magic number 0.4
let cardHeight = screenHeight * 0.4
// After: named constant
private let cardHeightRatio: CGFloat = 0.4
let cardHeight = screenHeight * cardHeightRatio
If the value can change between builds or environments, move it to a configuration file or application resources. For strings, use localization files. For URLs, use build config or xcconfig. For dimensions, use resource files (dimens.xml on Android). Verify that the application builds and works correctly after the extraction.
After refactoring, write a test that checks that the configuration loads correctly and that the values match expectations. If someone changes the config in the future, the test will indicate the discrepancy. A configuration test is a fast and reliable way to prevent regression.
After moving to config, verify that all places that used the old value now reference a single source. Remove commented-out code and old constants that are no longer used. Finalize the refactoring with a commit message describing which values were moved and where.
Frequently Asked Questions
Hardcode — rigidly writing a value in the source code instead of placing it in configuration or resources. This makes the code less flexible and more difficult to maintain.
Hardcode complicates changing application behavior, hinders testing, creates duplication, and increases the risk of copy-paste errors. Changing a hardcoded value requires rebuilding and redeploying the application.
Acceptable for mathematical constants, stable values that do not change during the application lifecycle, and temporary prototypes. In production, even constants should be extracted into named variables.
Find all magic numbers using search, replace them with named constants or move them to a configuration file. Write a test that verifies configuration loading. Remove duplicates and make a commit describing the changes.
A constant is a named value in code that can be changed in one place. Hardcode consists of unnamed values scattered throughout the code. Best practice: always use named constants with meaningful names.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also