Crossing our linguistic wiresLet’s imagine an imaginary scene from your company, Delorean (the Uber for time travel). You are a software engineer on the team responsible for writing software systems that handle e-commerce payment processing for your users, who pay for their rides from your company’s time-travel ride sharing service. You are discussing the implementation of a new set of features with your product owner (PO), and it unfolds like this:
PO: Our next big project is to update our driver app to show rider locations on the timeline map. You: And when do these riders show up on the timeline screen? PO: When the driver turns on the app and signals that she’s driving. You: OK, so that means when the app boots up and theYour poor product owner, removed from the direct details of implementation in your code, gets a hazy explanation of what’s actually going on. He vaguely remembers that the DriverStatus subsystem vaguely corresponds to drivers who use the mobile app and the HailingUser subsystem deals with riders who want rides, but he always gets them mixed up. Your system’s naming conventions are getting in the way, and they don’t correspond to any real-world concepts that he’s familiar with. Come to think about it, this same confusion played out among the team in the last iteration planning meeting, where you discussed the intricacies of a specific story:
DriverStatusservice receives a POST we’ll need to simultaneously fetch references from the
HailingUserservice based on time locality. PO: Um…
PO: In this story, we’re going to add a coupon box to the checkout flow. You: [Thinking out loud] Hm… would that mean we add aA completely different implementing engineer, of course, doesn’t read the note in the project backlog story (who has time to, anyways?). In the course of implementation, he gets tripped up in semantics and spends the better part of a half day re-implementing the
/couponroute to the
checkoutAPI? We could POST to it and then it will create
Coupons in the backend. Teammate: Wait – I think we call them
Discountsin the backend. And the checkout API is technically implemented by the
RideCommerceservice. You: Oh right, I forgot. Let’s make the route
/couponand it’ll create a
Discountobject. And in this story, let’s just remember that the Checkout API really refers to the
Checkoutflow as an entirely new service, before realizing his mistake in code review and backing out his changes. Months later, a new colleague is tasked to fix the link in the checkout flow, but files an incomplete fix because she was not aware of the fact that
Discounts. The bug makes its way to production, where the code begins causing havoc in your critical checkout systems.
What went wrong here?
- There was a different software system (RideCommerce) with a name that did not map cleanly back to the customer process (“checkout” flow).
- The system had two conflicting names for the same concept: Discount and Coupon.
- Extra documentation was created to note the discrepancies between concepts and names.
- This extra documentation and system knowledge did not propagate to all members of the team.
A better, Domain-Driven
Eric Evans’ book Domain-Driven Design introduces the concept of a Ubiquitous Language – a shared, common vocabulary that the entire team shares when discussing software.
This “entire team” is made up of designers, developers, the product owner and any other domain experts that exist at the organization. Take note that it is important that your team have a domain expert. Your product owner may be your domain expert (and typically is). However, you may have other domain experts such as:
- Any team that builds reporting or analytics off of your software.
- Upstream data providers.
- Anybody further up the reporting chain whose purview includes the software you’re building, or its effects. Think: the Director of Finance, the COO, the head of Customer Support.
- The users of your software.
Developing a Ubiquitous Language with a GlossaryGet your team together – the domain experts and the technical experts – and set them about creating a document that tracks your team’s language. This is the living document of all the terminology your team uses – along with all its definitions. This is known as the Glossary: From now on, use only the term definitions listed here in your stories both in person AND in your source code. Be explicit about how you use your language! I’ve been on many projects where the sloppy usage of a term from project inception led to the usage of that term in the code – codifying that messy, slippery term throughout the life of the project. The purpose of the Ubiquitous Language is to get the whole team on the same page – what each thing means, and bridge the gap between the jargon used on the development team and the customers. Your Glossary is a living document. It is meant to be living – either on a continually updated Google Doc or a wiki page. It should be visible for all to see – you should print it out and post it on the walls!
Refactoring your team to use the right termsA great time and place to start using this new Ubiquitious Language is in your everyday conversations with your domain experts, or with the technical team. Let’s listen in on a planning meeting with the product owner:
You: So when a User logs into the app and broadcasts that they’re ready to drive… PO: You mean Driver. When a Driver logs in. You: Right. Good catch.The little correction seems a little silly (after all, you both know only Drivers use the broadcast feature of the app), but the laser focus on using the right words means that your team is always on the same page when talking about things. As your team starts using the defined terms in the Glossary, the same words start to slip off your tongues, making working together easier than ever. Later that afternoon, your teammate taps you on the shoulder:
Teammate: I’m about to start working on the checkout flow. I suggest we rename the Discount class to Coupon. You: Great idea. That way, we aren’t tripped up by the naming mismatches in the future.Congratulations! Your team is making its first steps to Ubiquitous Language nirvana: your code needs to reflect your domain language.
Teammate: I do have a question about the coupon, though. Do you think it’s applied to the BookingAmount, or is it added? PO: [Overhearing conversation] You had it right. It’s applied.You and your teammate then go and update the glossary, scribbling an addendum on the wall (or updating your wiki): Future teammates will thank you for your precision with your terms.
Refactoring your code to use the right termsYour teammate and you then walk over to her desk; as a pair you proceed to refactor the existing account code. We’ll use Ruby for the sake of this example. In the beginning, the Checkout code looks like this. A pretty straightforward implementation of the parts of the checkout flow that apply a coupon. You take a first pass and rename the
Here’s the current concept of how the Checkout works:
Now there’s something funny here – your domain language suggests that a Coupon is applied to a BookingAmount. You pause, because the code reads the opposite – “A Coupon calculates its amount for a BookingAmount”.
You: How about we also refactor theIn your next refactoring pass, you move the
calculate_amount_formethod to reflect the language a little better? Teammate: Yeah. It sounds like the action occurs the other way – the BookingAmount is responsible for applying a Coupon to itself.
calculate_amount_formethod into the
BookingAmount, renaming it
applied_coupon_amount: Finally, you change your
Checkoutimplementation to match: Here’s the new diagram: Remember: we wanted our code to match the domain language: “A Coupon is applied to a BookingAmount“. When you read the implementation in plain English, it reads:
The Checkout‘s total amount is its BookingAmount minus its applied Coupon amount.Phew! Designing a strong Ubiquitous Language was hard work! In fact, you had spent a serious amount of time debating and clarifying with your domain experts:
- Is a Coupon applied to a BookingAmount, or is it added to one?
- Should we call the monetary value of a Coupon its amount, or cost?
- Is the pre-tax, pre-discount amount in the BookingAmount called a price, or a cost?
In closing…In this brief time we had together,
- We discussed why names are important – especially in a complex endeavour like software development.
- We covered why it’s important to arrive at a shared understanding, together as a team, using the same words and vocabulary.
- We discovered how to build and integrate a Glossary into the daily rhythm of our team.
- We refactored the code twice – illustrating how to get code in line with the domain language.