Constants in Golang are a fundamental feature. One of Go’s design goals is clarity: code should be predictable, readable, and safe. Constants support that philosophy because they represent values that must not change.
If a value is truly fixed (like a version string, a timeout limit, HTTP status code, or “magic number”), making it a constant communicates intent clearly. It also allows the compiler to catch mistakes early.
In Go, constants are evaluated at compile time. That means they are not like variables that hold changing state constants are “locked” values.

Constant Declaration
There are multiple ways to declare constants in Go. Below are the common styles, along with when to use each one.
Declare a single constant
The simplest form: declare one constant with const.
Syntax: const <name> = <value>
const AppName = "JyotishLabs"
const MaxRetries = 3
const Debug = false
This method is used in the following scenarios:
- When a value should never change across the program.
- When you want to remove “magic numbers” from the code.
- When the value is shared across functions/files in the same package (package-level constants).
- When the value is part of documentation (readability) as much as functionality.
Declare with explicit type (typed constants)
You can explicitly assign a type to a constant. This makes the constant stricter and prevents accidental mixing.
Syntax: const <name> <type> = <value>
const Port int = 8080
const Pi float64 = 3.1415926535
const AppCode string = "JYOTISH-001"
This method is used in the following scenarios:
- When you want to lock a constant to a specific type for APIs or struct fields.
- When type clarity matters in a public package (readability for other developers).
- When you want to avoid implicit conversions in math-heavy logic.
Declare without explicit type (untyped constants)
By default, constants can be untyped. This is a very Go-specific feature and it’s extremely useful.
const Limit = 10 // untyped integer constant
const Rate = 2.5 // untyped floating-point constant
Because they are untyped, Go can “fit” them into different types when needed:
const Limit = 10 var a int = Limit var b int64 = Limit var c float64 = Limit
This method is used in the following scenarios:
- When you want flexibility without writing type casts everywhere.
- When the constant is a general numeric value and the actual type depends on usage.
- When writing clean beginner-friendly examples.
Constant block (grouped constants)
A const block lets you group related constants like a configuration section. This is the cleanest style when you have multiple fixed values.
const (
Host = "localhost"
Port = 8080
Debug = false
MaxRetries = 3
)
This method is used in the following scenarios:
- Reads like a config section.
- Keeps related constants together and makes the file easier to scan.
- Common at package level (shared constants).
Constants from expressions (compile-time only)
Constants can be derived using expressions—as long as the compiler can evaluate them at compile time.
const (
KB = 1024
MB = KB * 1024
GB = MB * 1024
)
const (
DefaultTimeoutSeconds = 30
DefaultTimeoutMillis = DefaultTimeoutSeconds * 1000
)
This method is used in the following scenarios:
- Unit conversions (KB/MB/GB, seconds/millis).
- Defining limits in a readable and maintainable way.
- Avoiding repeated calculations and magic numbers.
What cannot be a constant
Constants must be compile-time values. Anything that needs runtime evaluation cannot be a constant.
❌ Not allowed:
// const Now = time.Now() // runtime value
// const IDs = []int{1, 2, 3} // slices are runtime structures
// const M = make(map[string]int) // maps are runtime structures
✅ Use var instead for runtime values.
This rule exists because constants are embedded by the compiler and don’t “live” as runtime objects like maps or slices.
iota (enums and sequences)
Go doesn’t have “enums” like Java, but iota gives you a clean way to create enum-like constants.
iota starts at 0 inside a const block and increments automatically.
Basic example
const (
Pending = iota // 0
Running // 1
Done // 2
)
This method is used in the following scenarios:
- Defining states (Pending/Running/Done).
- Defining fixed modes (Dev/Staging/Prod).
- Avoiding manual numbering mistakes.
Start from 1 (common style)
const (
StatusNew = iota + 1 // 1
StatusOpen // 2
StatusClosed // 3
)
Used when:
- You want 0 to mean “unknown/unset”.
- You are mapping values to external systems where 0 is reserved.
Bit flags with iota (advanced but very common)
const (
Read = 1 << iota // 1
Write // 2
Exec // 4
)
Used when:
- You want combinable flags like permissions:
- Read | Write
- Read | Exec
- Feature toggles and capability flags.
Scope rules (where constants can be declared)
Constants can be declared:
- At package level (shared across the package)
- Inside functions (local to the function)
package main
const Version = "1.0.0" // package-level
func main() {
const Greeting = "Hello" // local
_ = Greeting
}
Best practices for constants
- Prefer constants for fixed values (avoid magic numbers).
- Use
const (...)blocks for grouped values. - Use meaningful names:
MaxRetries,DefaultTimeoutSeconds,StatusOpen. - Use
iotafor sequences/states, but don’t overuse it if it hurts readability. - Keep constants close to where they matter (package-level for shared values, function-level for local logic).
Quick mental model: const vs var
Use const when the value is fixed at compile time.
Use var when the value can change or is determined at runtime (config, env, DB, time, user input).



