diff --git a/categories/software-engineering/rules-to-better-domain-driven-design.md b/categories/software-engineering/rules-to-better-domain-driven-design.md index 97c0f635aa..1576cf3d5a 100644 --- a/categories/software-engineering/rules-to-better-domain-driven-design.md +++ b/categories/software-engineering/rules-to-better-domain-driven-design.md @@ -7,6 +7,7 @@ index: - encapsulate-domain-models - use-specification-pattern - anemic-vs-rich-domain-models +- ubiquitous-language --- Domain-Driven Design (DDD) is a software development approach that focuses on the domain of the problem, rather than the technology. It is a way of thinking and a set of priorities, aimed at accelerating software projects that have to deal with complex domains. diff --git a/rules/avoid-generic-names/rule.md b/rules/avoid-generic-names/rule.md index adee8cf9f8..e6cc80d90b 100644 --- a/rules/avoid-generic-names/rule.md +++ b/rules/avoid-generic-names/rule.md @@ -29,18 +29,30 @@ Generic names like “manager”, "helper", “processor”, “data”, and “ ## Why generic names are problematic Words like "manager" and "processor" imply something that handles various tasks, which can make it tempting to pile unrelated responsibilities into one class. "Helper" makes this worse as it becomes a catch-all for a collection of disorganized functionality.Names like "data" or "info" are similarly ambiguous, as they could apply to nearly anything, from a database connection to a simple string. Specific names are always preferable, as they make the code easier to understand and prevent code bloat from accumulating unrelated functionality. -:::bad +:::greybox Imagine you’re writing a class to handle orders in an e-commerce system. You name it `OrderManager`. While this name suggests that it might have something to do with orders, it doesn’t clarify how it interacts with them. Is it creating orders, updating them, processing payments, or all of the above? The generic term “manager” gives us no clear indication. ::: +:::bad +Generic names only tell you what part of the domain or code base a class or method works with, not what it does +::: -:::good +:::greybox A better name could be `OrderProcessor`, but if the class specifically handles only one aspect — say, sending orders for shipment — a more precise name would be `ShippingOrderHandler` or `OrderShipmentService`. This name directly reflects its purpose, making it immediately clear what the class is responsible for. ::: +:::good +Specific names are better than generic names +::: -:::bad +:::greybox Let’s say you’re building a system to track medical records, and you create a class called `PatientData`. The name could apply to anything — health information, appointment history, test results. There’s no way to tell what role this class actually plays. ::: +:::bad +The name 'data' could literally mean just about anything +::: -:::good +:::greybox If the class is responsible for managing a patient’s appointment history, a more accurate name could be `PatientAppointmentHistory`. This name immediately tells us the scope and purpose of the class without relying on catch-all terms. ::: +:::good +A class name for the specific data that it represents is much easier to understand +::: diff --git a/rules/avoid-micro-jargon/rule.md b/rules/avoid-micro-jargon/rule.md index 1af52a5f2a..edbb6bf623 100644 --- a/rules/avoid-micro-jargon/rule.md +++ b/rules/avoid-micro-jargon/rule.md @@ -28,7 +28,7 @@ Code that relies on nicknames, abbreviations, or internal jokes can create unnec :::greybox **How this differs from ubiquitous language** -Using ubiquitous language is about naming concepts in ways that align with terms used by domain experts and stakeholders. While this might seem like micro-culture jargon at first glance, it’s different for an important reason. Unlike insider terms, ubiquitous language refers to widely recognized ideas within the domain, making code clearer for anyone familiar with the field. Avoid in-grouped terms that don’t convey meaning to people outside the team, even if they seem descriptive to those who are “in the know.” +Using ubiquitous language (see our rule [Do you use ubiquitous language?](/ubiquitous-language)) is about naming concepts in ways that align with terms used by domain experts and stakeholders. While this might seem like micro-culture jargon at first glance, it’s different for an important reason. Unlike insider terms, ubiquitous language refers to widely recognized ideas within the domain, making code clearer for anyone familiar with the field. Avoid in-grouped terms that don’t convey meaning to people outside the team, even if they seem descriptive to those who are “in the know.” ::: :::bad diff --git a/rules/avoid-using-your-name-in-client-code/rule.md b/rules/avoid-using-your-name-in-client-code/rule.md index 48f1e342b5..6bd103ae2a 100644 --- a/rules/avoid-using-your-name-in-client-code/rule.md +++ b/rules/avoid-using-your-name-in-client-code/rule.md @@ -29,12 +29,18 @@ When building solutions for clients, it’s tempting to include personal or comp ## Why It Matters Names that reflect the client’s brand or domain are clearer and more meaningful for the client’s internal teams and stakeholders. They also reduce the risk of naming conflicts and make it easier for future developers to understand the purpose of a component in context. -:::bad +:::greybox Naming a custom entry field `GoldieEntry` or `SSWEntry` might make sense in a personal or shared library but is out of place in a client project. ::: +:::bad +Naming client IP after yourself or your company is not cool +::: -:::good +:::greybox Instead, using something like `NorthwindStepper` is more client-aligned and indicates that this is a customized variation of a standard control. +::: +:::good +Naming client IP with the client's branding is better +::: **Note:** This approach is ok to denote a branded version of something, but often it's better to indicate the customization itself in the name. See our rule [Do you use meaningful modifiers for custom implementations?](/use-meaningful-modifiers). -::: diff --git a/rules/consistent-words-for-concepts/rule.md b/rules/consistent-words-for-concepts/rule.md index 03e7b78582..2f5d5f1ded 100644 --- a/rules/consistent-words-for-concepts/rule.md +++ b/rules/consistent-words-for-concepts/rule.md @@ -37,13 +37,14 @@ Imagine you’re working on an e-commerce app, and you’ve been tasked with add Later, you open a pull request, and a colleague calls, confused, asking why you’ve re-implemented an entire feature. After some back and forth, they show you the existing `SendOrder` method, which already handles notifications. -:::bad ```csharp public void SendOrder(NotificationType type) { // existing implementation } ``` +:::bad +The name used for this method is not consistent with the name used for the same concept everywhere else in the code base ::: ## Outcome @@ -53,17 +54,18 @@ In this case, the terms “order” and “consignment” may seem related, but In this scenario, the `SendOrder` method should have been called `SendConsignment`, assuming “consignment” was already used in the codebase. -:::good ```csharp public void SendConsignment(NotificationType type) { // new implementation } ``` +:::good +The name used for this method is the same name used for this concept throughout the code base ::: -To clarify, it's not necessarily wrong to have a `SendOrder` method, if the term order is ubiquitous. It might represent a pipeline for example, tracking a workflow from submission by the customer to receipt by the customer, and everything in between. But if “order” was the chosen term, the team should have used it consistently across the code. Any introduction of new terminology, such as “consignment,” should be a proactive, team-wide decision that includes any necessary refactoring. +To clarify, it's not necessarily wrong to have a `SendOrder` method, if the term order is ubiquitous (see our rule [Do you use ubiquitous language?](/ubiquitous-language)). It might represent a pipeline for example, tracking a workflow from submission by the customer to receipt by the customer, and everything in between. But if “order” was the chosen term, the team should have used it consistently across the code. Any introduction of new terminology, such as “consignment,” should be a proactive, team-wide decision that includes any necessary refactoring. ## DRY Principle Implications In a worst-case scenario, someone unfamiliar with the `SendOrder` method might merge the `SendConsignment` code without realizing it’s redundant. Now, two methods exist for the same function — each handling notifications differently. This violates the DRY principle, as you now have two distinct pieces of knowledge on handling order shipments, potentially leading to divergent behavior and increased maintenance overhead. diff --git a/rules/ubiquitous-language/rule.md b/rules/ubiquitous-language/rule.md new file mode 100644 index 0000000000..1b6855f9e0 --- /dev/null +++ b/rules/ubiquitous-language/rule.md @@ -0,0 +1,49 @@ +--- +seoDescription: We often use ours or our company name to denote a custom version of something. Unless you're publishing a library, this is never a good idea. +type: rule +archivedreason: +title: Do you use ubiquitous language? +uri: ubiquitous-language +created: 2024-11-05T00:00:00.0000000Z +authors: + - title: Matt Goldman + url: https://ssw.com.au/people/matt-goldman +related: + - encapsulate-domain-models + - use-specification-pattern + - anemic-vs-rich-domain-models + - consistent-words-for-concepts + - avoid-micro-jargon + - when-to-use-technical-names +guid: f8555180-50c2-423e-84e2-d73a9018222f +--- + +Ubiquitous language is a core principle in domain-driven design (DDD) that encourages developers and stakeholders to use the same vocabulary when discussing business logic and domain concepts. By using a shared, domain-specific language across code, documentation, and conversations, you ensure that everyone has a common understanding of core concepts. This approach reduces misunderstandings and makes the codebase more accessible to those familiar with the business domain. + + + +## Why Ubiquitous Language Matters +Ubiquitous language helps bridge the gap between technical and non-technical stakeholders, creating a consistent and clear understanding of the domain. When everyone uses the same terms — whether in code, documentation, or discussions — it’s easier to align on requirements, troubleshoot issues, and onboard new team members. Without it, terms can become muddled, causing confusion and misinterpretation. + +:::greybox +Let’s say you’re working on an insurance system, and the domain term “policyholder” is used consistently among stakeholders. However, in the codebase, you see different terms used interchangeably: `AccountOwner`, `Customer`, and `InsuredParty`. Each of these terms could technically represent the policyholder, but the inconsistency can lead to confusion and misunderstandings about the exact role of each entity. +::: +:::bad +Terms in the code do not reflect domain language used by stakeholders +::: + +:::greybox +To follow ubiquitous language, you would use `PolicyHolder` consistently across the codebase, aligning the code’s vocabulary with the language used by domain experts. This approach eliminates ambiguity, making it clear that `PolicyHolder` refers to the specific entity recognized by all stakeholders. +::: +:::good +Ubiquitous language is used, and developers and stakeholders are on the same page +::: + +### Benefits +* **Improved Communication:** By using the same terms as domain experts, developers and stakeholders communicate more effectively, reducing the risk of misinterpretation. +* **Increased Readability:** Consistent terminology makes it easier for anyone familiar with the domain to understand the codebase. +* **Enhanced Maintenance:** When domain terms are used uniformly, developers spend less time deciphering concepts and more time building functionality. + +:::greybox +💡 **Tip:** You can use the [Contextive](https://github.com/dev-cycles/contextive) extension for IntelliJ and VS Code (other IDEs coming soon) to assist with this. The linked repo also has a discussion between Chris Simon (the author) and [SSW's Gert Marx](https://www.ssw.com.au/people/gert-marx/) about both the extension and ubiquitous language in general +::: diff --git a/rules/use-clear-meaningful-names/rule.md b/rules/use-clear-meaningful-names/rule.md index bb31b5c916..7688a1dd82 100644 --- a/rules/use-clear-meaningful-names/rule.md +++ b/rules/use-clear-meaningful-names/rule.md @@ -34,32 +34,50 @@ It’s easy to invent clever codes to keep names short, like `cstmrMgr` to repre ## Be verbose Today, there’s no reason to squeeze names into character limits or cryptic codes. While you shouldn’t use full sentences for a class or method name (test cases might be an exception, see our rule [Do you follow naming conventions for tests and test projects?](/follow-naming-conventions-for-tests-and-test-projects)), full, meaningful words make your code far more readable and maintainable. -:::bad +:::greybox Imagine you’re creating a game and need a variable to store the player’s health. You might decide to use an integer called `NRG`. It’s short for "energy," which might seem clever — it’s easy to write, and you know what it means. But this has some problems: * Other developers might misinterpret "energy" as something else, like power or ammo. Wouldn't "health" better represent the intended meaning here? * If you add enemies with their own energy, what will you name their variable? (Spoiler: `nmeNrg`.) ::: +:::bad +Using clever abbreviations is unnecessary, and can easily cause confusion +::: -:::good +:::greybox A better name for this variable is `PlayerHealth`. It clearly describes the specific kind of "energy" (health) and who it belongs to (the player), making it instantly understandable to anyone reading the code. ::: +:::good +A clear and precise name avoids confusion and conveys its meaning clearly +::: -:::bad +:::greybox Now let’s say you’re working on an invitation and activation feature. You need a variable to store the validity period for an invitation, so you create one called: `ttlDays`. While `ttlDays` might seem logical as shorthand for "time-to-live in days," other developers might interpret it as "total days," or spend extra time deciphering its intended use. ::: +:::bad +Unnecessary abbreviations can often be ambiguous +::: -:::good +:::greybox Just call it `TimeToLiveInDays`. It leaves no room for misinterpretation and makes the purpose of the variable obvious on sight. ::: +:::good +Use a clear, unambiguous name +::: -:::bad +:::greybox Consider a class named `UserValidator`. At first glance, it seems sensible — it’s presumably responsible for validating a user. But when you think about it, "UserValidator" doesn’t really tell us much. What exactly is it validating? Is it responsible for validating login credentials, profile information, or something else entirely? ::: +:::bad +Vague or generic names that lack specificity can easily lead to confusion +::: -:::good +:::greybox A clearer approach is to ensure each class has a specific responsibility. If it’s meant to handle all user-related validation rules, something like `ValidationHandler` indicates it’s an engine responsible for executing multiple rules and returning a result. But if the class is responsible for validating only a specific aspect of a user, a name like `UserNameLengthValidator` makes its role unmistakable; it tells us this class should validate only the length of a username. ::: +:::good +More specific names convey clearer meaning with greater precision +::: :::info **Remember:** Names should describe what something represents without mental gymnastics to decode it. A clear, meaningful name is one of the simplest ways to make your code more readable, maintainable, and welcoming for future collaborators (and for yourself). diff --git a/rules/use-meaningful-modifiers/rule.md b/rules/use-meaningful-modifiers/rule.md index d164fad707..fc05955940 100644 --- a/rules/use-meaningful-modifiers/rule.md +++ b/rules/use-meaningful-modifiers/rule.md @@ -26,12 +26,18 @@ When creating custom controls, avoid vague or generic names like CustomStepper o ## Why It Matters -Meaningful modifiers make it clear what a class, module, or compoent does or how it differs from a standard version. This helps developers understand its role without digging through code and reduces the chance of naming conflicts with other libraries. +Meaningful modifiers make it clear what a class, module, or component does or how it differs from a standard version. This helps developers understand its role without digging through code and reduces the chance of naming conflicts with other libraries. -:::bad +:::greybox Naming a custom `DbContext` implementation `CustomDbContext` or a specialized input control `CustomInput` doesn’t provide any real information. It’s unclear what is customized, making it harder to understand and maintain. ::: +:::bad +A modifier that doesn't tell you what has been modified is of no value +::: -:::good +:::greybox A more descriptive name, like `WriteOnlyDbContext` or `BorderlessTextInput`, indicates exactly what’s different about the component, making it easier to understand its function at a glance. ::: +:::good +A modifier that clearly conveys what is different from the original can save developers time reading through the code +::: diff --git a/rules/use-nouns-for-class-names/rule.md b/rules/use-nouns-for-class-names/rule.md index c485c0c544..9f23b6a0b1 100644 --- a/rules/use-nouns-for-class-names/rule.md +++ b/rules/use-nouns-for-class-names/rule.md @@ -28,13 +28,20 @@ A fundamental principle of object-oriented programming is that a class represent ## What is a noun? A noun is a word that names a thing. Common examples include tangible items like Table, Cup, or Computer and abstract entities like Record, Database, or Cloud. In your code, class names should clearly reflect what they represent, giving developers an immediate understanding of the class’s role. -:::bad +:::greybox Imagine you’re building an e-commerce application, and you create a class called `ProcessOrder`. While it might seem reasonable, this name is misleading — it suggests an action, not a thing. The class’s responsibility is not the act of processing but the concept of an Order itself. ::: +:::bad +A class that appears from its name to be an action is confusing +::: -:::good +:::greybox Naming the class `Order` better represents its role as an entity that holds order-related data and behavior. Later, if you need to perform an action on the order, you might create a `ProcessOrder` method within the`OrderService` or `OrderProcessor` class (see our rule [Do you use verbs for method names?](/verbs-for-method-names)). This keeps the naming consistent with object-oriented principles. ::: +:::good +A class name that clearly represents a thing is much easier to understand +::: + ## Events: The exception that proves the rule In domain-driven design (DDD) and event-driven architectures (EDA), you’ll often see exceptions to this rule. Events like `OrderPlaced` or `UserRegistered` are commonly named with verb phrases to represent specific actions or occurrences within the system, rather than entities with persistent state. This naming convention is acceptable, as it indicates a change or trigger rather than a static object. For other class types, however, sticking to nouns keeps the codebase clear and consistent. diff --git a/rules/verbs-for-method-names/rule.md b/rules/verbs-for-method-names/rule.md index 608adb4fd0..a529d3965e 100644 --- a/rules/verbs-for-method-names/rule.md +++ b/rules/verbs-for-method-names/rule.md @@ -28,18 +28,30 @@ Code becomes easier to understand when names align closely with their meaning. S ## What is a verb? A verb is a word that describes an action or process—something that’s done. Examples include walk, run, think, listen, and breathe, as well as process, calculate, send, and save. While your method names shouldn’t all be single verbs (since that’s often too vague), they should be verb-based, using a verb as the foundation of their meaning. -:::bad +:::greybox Imagine you’re working on an e-commerce app and need to write code to handle shipping products to customers who have completed an order. You create a method called `Ship`. While Ship is a verb, it’s also a noun, making it ambiguous. Additionally, it’s not descriptive enough to convey the method’s purpose clearly. ::: +:::bad +A method represents an action, so naming it like a thing will cause confusion +::: -:::good +:::greybox A better name for this method is `SendCustomerOrder`. It’s specific, making it clear what the method does and within what context. ::: +:::good +A method that is named as a verb clearly tells you that it does something, and what that thing is +::: -:::bad +:::greybox Suppose you’re working on an electronic medical record system, and you need to create a way for nurses to document medications that have been administered to patients. You name the method `Administration`. This is problematic for two reasons — it’s a noun, and it’s ambiguous. “Administration” has multiple meanings, and the method name doesn’t make it clear what this action involves. ::: +:::bad +A method that not only isn't a clear action, but uses a word that needs context (and none is provided) is guaranteed to cause confusion +::: -:::good +:::greybox A more precise name for this method is `AdministerMedication`. It clearly describes the real-world action it models. Another option could be `RecordMedication`, but this is less effective. First, “record” can be either a noun or a verb, and second, it lacks context — it could imply logging delivery to a pharmacy or entering a new medication type into the system. ::: +:::good +Method names that describe the action and provide any needed context help avoid confusion +::: diff --git a/rules/when-to-use-technical-terms/rule.md b/rules/when-to-use-technical-terms/rule.md index cdef8d98a4..7f44c2489d 100644 --- a/rules/when-to-use-technical-terms/rule.md +++ b/rules/when-to-use-technical-terms/rule.md @@ -27,23 +27,35 @@ Naming is most effective when it aligns with the audience’s understanding. In ## Choosing the Right Term ### Use Domain Terms for Business Logic -When naming classes, methods, or variables that represent core business concepts, use terms that reflect the language of the domain. This approach, often called *ubiquitous language*, helps ensure that the code is understandable to developers and domain experts alike, reducing the risk of misinterpretation. For example, in a retail application, classes like `Order`, `ProductCatalog`, and `CustomerAccount` use domain terms that match stakeholders' understanding. +When naming classes, methods, or variables that represent core business concepts, use terms that reflect the language of the domain. This approach, often called *ubiquitous language* (see our rule [Do you use ubiquitous language?](/ubiquitous-language)), helps ensure that the code is understandable to developers and domain experts alike, reducing the risk of misinterpretation. For example, in a retail application, classes like `Order`, `ProductCatalog`, and `CustomerAccount` use domain terms that match stakeholders' understanding. ### Use Technical Terms for Implementation Details -Conversely, use technical terms for internal or lower-level code that doesn't directly involve business logic. These terms should clearly describe the technical functionality, helping developers quickly understand the purpose without needing domain context. For instance, classes or methods named `CacheIntercepter`, `AnalyticsLogger`, or `CustomerRepository` make sense to developers without domain knowledge, focusing instead on their technical purpose. +Conversely, use technical terms for internal or lower-level code that doesn't directly involve business logic. These terms should clearly describe the technical functionality, helping developers quickly understand the purpose without needing domain context. For instance, classes or methods named `CacheInterceptor`, `AnalyticsLogger`, or `CustomerRepository` make sense to developers without domain knowledge, focusing instead on their technical purpose. -:::bad +:::greybox Let’s say you’re building an online banking system and need a class to manage the process of logging customer transactions. You name the class `TransactionProcessor`. This name is technically accurate, but it doesn't align well with banking terminology. “Processor” is too generic for the banking context and may not resonate with stakeholders, potentially leading to misunderstandings. ::: +:::bad +Using a technical term that has a potential meaning in the domain context is bound to cause confusion +::: -:::good +:::greybox A better name might be `TransactionLedger` or `TransactionLog`, aligning the term with common banking concepts. This shift helps communicate to both developers and business stakeholders that this component handles logging and auditing of transactions, not merely processing. ::: +:::good +Using domain language helps reflect the business value of the code, and helps all stakeholders (technical and non-technical) work together with a shared understanding +::: -:::bad +:::greybox In a retail application, you create a class called `CatalogFetcher` to retrieve the list of products. The name describes its technical function, but it doesn’t align with domain language. “CatalogFetcher” sounds more like an internal utility than a core business concept. ::: +:::bad +Names that describe what something does at a technical level are only valuable for low-level functionality +::: -:::good +:::greybox A clearer, domain-aligned name would be `ProductCatalog`. This name reflects the business term and would be immediately recognizable to anyone familiar with the retail context. ::: +:::good +Code that provides business value is expressed in business terms +:::