June 18, 2019
When learning a new language it’s always interesting to see how it changes your perspective. It can show you how little you know, show you how entrenched you are in your beliefs or open you up to a whole new world!
I’ve written bits of terraform for a while but have only recently started doing more. It’s easy to simply continue copying and pasting other’s code, with small edits. I followed their patterns and basically did just enough to make it work.
But now, starting to write modules, refactoring other’s code as I start to consume modules developed by “real” platform engineers, I’ve learnt a few things which will probably affect the way I write Java.
Terraform has the concept of modules, which have inputs (like parameters or arguments) and outputs (like return values but you can have multiple and they’re named). Modules are used to refactor out repeated declarations so there is less repetition and more abstract concepts can be built with less. For example, imagine declaring a terraform resource is like laying a brick when building a house. And then a module would be creating a wall made of loads of bricks, which can be reused in multiple place to make the house (you also have to imagine you can copy and paste walls in this metaphor). In the example we have 3 levels of abstraction - brick, wall and house. Modules are made to abstract away the resources in the level below it.
The biggest design decisions when writing terraform seem to be around inputs and outputs, what do you name them, what type are they.
This lead me to think, “What I’m really designing is interfaces between modules”. Which lead to the bigger question, what interface should this module provide? Each module sits at a different level of abstraction e.g. the provided resources are firmly at the infrastructure level, they create VMs, users and file storage containers. Developer created modules sit at any level above that e.g. organisation level modules where all VMs for your organisation need a certain type of auditing, or application level modules which provision your databases with particular application settings.
Which lead me to then think, in order to define sensible interfaces, name things well, and determine the right inputs I need to understand what this module should know about. For example does it know about kubernetes worker security groups? (Security groups (SG) are like a firewall definition, it includes the which ports are allowed for inbound and outbound traffic) If it did then it could just use the security group for all the external services to allow communication where needed. Or should it have a separate security group input for each service requiring tcp communication? For example, a Postgres SG, a Redis SG, a RabbitMQ SG - even if all of the values for these inputs end up being the same value (the worker security group). This sort of decision is the difference between having an input for the module
k8s_worker_security_group and having
postgres_security_group,redis_security_group,rabbitmq_security_group, you can choose to have less inputs or a more descriptive interface and better encapsulation.
Which leads to the ultimate question which is what changes are you isolating? In the previous example, by not having the module know about Kubernetes worker security groups I’m abstracting the module away from being run on Kubernetes. If I wanted to run it on Docker Swarm (scream emoji) then I shouldn’t need to change the module input variable names.
Isolating what changes is a key foundation of the Gang of Four’s Design Patterns and something I learnt long ago but have recently not been conscious of in my Java coding. Thanks to Terraform I’m going to be more cognisant of the interfaces I’m creating, the knowledge I’m encoding into each class and the changes I’m isolating.
I recognise that this thought process is firmly based in my Java experience - Java engineers often consider interfaces and encapsulation. It’s good to see principles that are ubiquitous, not just language specific. I’m sure I’ll relearn this principle to a deeper level again when I try another new language or framework.
When you last learnt a new language what did it teach you?
Written by Phil Hardwick