Programming in the small – less is more
Think about the time you learned to program. You wrote little programs that accepted simple input, did a little processing, and produced some output. Life was good. You could hold the entire program in your head.
You understood every line of code. Debugging and troubleshooting was easy. For example, consider a program to convert temperatures between Celsius and Fahrenheit:
package main
import (
"fmt"
"os"
"strconv"
)
func celsius2fahrenheit(t float64) float64 {
return 9.0/5.0*t + 32
}
func fahrenheit2celsius(t float64) float64 {
return (t - 32) * 5.0 / 9.0
}
func usage() {
fmt.Println("Usage: temperature_converter <mode> <temperature>")
fmt.Println()
fmt.Println("This program converts temperatures between Celsius and Fahrenheit")
fmt.Println("'mode' is either 'c2f' or 'f2c'")
fmt.Println("'temperature' is a floating point number to be converted according to mode")
os.Exit(1)
}
func main() {
if len(os.Args) != 3 {
usage()
}
mode := os.Args[1]
if mode != "f2c" && mode != "c2f" {
usage()
}
t, err := strconv.ParseFloat(os.Args[2], 64)
if err != nil {
usage()
}
var converted float64
if mode == "f2c" {
converted = fahrenheit2celsius(t)
} else {
converted = celsius2fahrenheit(t)
}
fmt.Println(converted)
}
This program is pretty simple. It does a decent job of validating its input and displaying usage information if something goes wrong. The actual computation the program does is just two lines of code that convert the temperature, but it is 45 lines long. There aren't even any comments. Yet, those 45 lines are pretty readable and easy to test. There aren't any third-party dependencies (just the Go standard library). There is no IO (files, databases, network). There is no need for authentication or authorization. There is no need to rate limit calls. There is no logging, no metrics collection. There is no versioning, health checks, or configuration. There is no deployment to multiple environments and no monitoring in production.
Now, consider integrating this simple program into a big enterprise system. You'll have to take into account many of these aspects. Other parts of the system will start using the temperature conversion functionality. Suddenly, the simplest operations might have cascading impacts. Changes to other parts of the system might affect the temperature converter:
This jump in complexity is natural. Large enterprise systems have many requirements. The promise of microservices is that by following proper architectural guidelines and established patterns, the additional complexity can be neatly packaged and used across many small microservices that work together to accomplish the system goals. Ideally, service developers can be shielded from the encompassing system most of the time. However, it takes a lot of effort to provide the right degree of isolation and still also allow for testing and debugging in the context of the entire system.