How DDD has enabled more productive conversations and cleaner code
My team at CircleCI, which focuses on plans and payments, has gone through many organizational changes over the last year as we’ve grown. One thing that has helped us maintain a sense of consistency throughout these changes is to adopt a different approach to writing software: Domain-Driven Design, also known as DDD.
Defining Domain-Driven Design succinctly is difficult, but the short answer is that it’s a method of connecting software architecture and relevant domains by using a universally accepted model. In other words, the software matches the domain. There are many resources available that define DDD and go into great detail, but for my team, there are two concepts that have been immensely helpful to us: defining our Bounded Contexts and our Ubiquitous Language.
Bounded Contexts and Ubiquitous Language
As you may know, Bounded Contexts are discrete pieces of a domain with clear boundaries. Bounded Contexts can contain one or many domains, and these domains can have nested domains which are known as sub-domains. A collection of Bounded Contexts is called a context map. Lastly, all contexts have a corresponding Ubiquitous Language: a collection of explicitly defined terms within a context. These terms should be used by all members of the team and have proper representation in the code as well. For example, in the Plans and Payments context, a “User” is anyone or anything that triggers a job, but in several other contexts at CircleCI, “User” is actually synonymous with a customer. Hopefully you were already thinking it, but “User” is actually not a great term to include in a ubiquitous language. It’s vague and imprecise. But as you can imagine, once a term like this is in use, it’s hard to get away from. This is why you should start thinking about Ubiquitous Language from the start if possible.
An example of the context map for the Plans and Payments team
Both these concepts are important because they ensure all members of a team are actually talking about the same things. By removing ambiguity, Ubiquitous Language allows more meaningful discussions. Bounded Contexts strive to give us a clear picture on where code resides and where our efforts should be focused. In our team, both of these worked as intended.
Benefits for our team
There are many issues on my team that have been greatly improved by implementing DDD. First: over time we’ve had numerous definitions for a person or thing that triggers a build: “Actor,” “Active User,” “User,” etc. and more than once, this has caused confusion for us when discussing this feature. Are we talking about commit authors? Are we talking about the Github PR owner? Are we talking about bots that trigger builds? The worst part is that if we’re confused, then what chance do our customers have? And what chance does our code have of being correct?
Surprisingly, we discovered that the definition of the User entity didn’t matter as much for us since it’s not relevant to our contexts. In fact, all we need to understand is that when a customer starts a build, we just need to count unique customers so we can track the number of seats they’re using. Defining our Ubiquitous Language has eased the process of naming variables, creating Jira tickets, writing documentation, and onboarding because all terms are streamlined and accepted by the team.
Establishing our Bounded Contexts has had many advantages as well. Moving our code as close as possible to the product has removed much of the difficulty associated with deciding where code should live. Additionally, because we decided on our contexts as a team, getting consensus on technical decisions is usually trivial. Best of all, we now have a conceptual model of how our code is actually written which has enabled us to actively engage and contribute to domain discussions and decisions, from architecture to defining our product roadmap.
Giving DDD a try
I can’t speak for the experiences of others, but despite only a cursory knowledge of DDD, I’ve absolutely felt a difference in productivity on my team. In exchange, all we had to do was spend a week agonizing and stumbling around a sea of post-it notes and whiteboards. We still have a long way to go, but luckily with DDD, nothing is immutable and our definitions can evolve with our domain. Seriously though, if you haven’t already, I highly recommend taking the time, digging in, and codifying your domain. The cohesion between your code and your product is worth it.
[VIDEO] CircleCI CTO Rob Zuber on Ubiquitous Language and Throughput