2 minutes reading time
When building real-world applications, configuration management is one of the first practical problems you run into. Whether it's ports, feature flags, or environment-specific settings, having a clean way to read and work with configuration is essential.
In this post, we'll walk through a simple yet practical Go program that demonstrates three important concepts:
We want to:
We start by defining a struct that mirrors our JSON:
type Config struct {
AppName string
Port int
Debug bool
Tags []string
IsDeprecated bool
}This struct acts as the blueprint for our configuration file.
Instead of tightly coupling our code to the struct, we define an interface:
type AppCfg interface {
Summary() string
Deprecated() bool
}Now we implement this interface on our Config struct:
func (c Config) Summary() string {
return fmt.Sprintf(`
App's Name : %s
Port : %d
Debug Mode : %t
Tags : %v
Deprecated : %t
`, c.AppName, c.Port, c.Debug, c.Tags, c.IsDeprecated)
}
func (c Config) Deprecated() bool {
return c.IsDeprecated
}Go interfaces are implemented implicitly, which keeps things simple and flexible.
We define a function that works with the interface rather than the struct:
func printAppCfg(apcfg AppCfg) {
fmt.Println(apcfg.Summary())
if apcfg.Deprecated() {
fmt.Println("App has been deprecated!!")
}
}This makes the function reusable and easier to test.
Now comes file reading and JSON parsing:
func readConfigFile(path string) (Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return Config{}, fmt.Errorf("readConfigFile -- Failed to read file -- %w", err)
}
cfg := Config{}
if err := json.Unmarshal(data, &cfg); err != nil {
return Config{}, fmt.Errorf("readConfigFile -- Invalid JSON -- %w", err)
}
return cfg, nil
}Key takeaways:
os.ReadFile simplifies file handlingjson.Unmarshal maps JSON into structs%w for better debuggingIn main, we fail fast if something goes wrong:
func main() {
cfg, err := readConfigFile("conf.json")
if err != nil {
fmt.Println("main --", err)
os.Exit(1)
}
printAppCfg(cfg)
}This approach ensures that errors are visible and don't silently break the application.
Here's a sample conf.json:
{
"AppName": "MyApp",
"Port": 8080,
"Debug": true,
"Tags": ["go", "backend", "demo"],
"IsDeprecated": false
}This small example brings together some core Go concepts:
Even though it's simple, these patterns scale really well in larger systems.
You can extend this by:
Simple, practical, and very Go.