# How to describe the behavior (functionality) of a software system How to describe the behavior of a software system -- what it does in the world? All thinking about software revolves around this question, from the earliest phases of design when we're asking what the behavior should be, to diagnosing failures in deployed systems when we're asking what happened. Describing behavior has three parts. First, we need to identify the phenomena: what the behavior is about. Second, using those phenomena, we can tell stories about them: how one leads to another, what happens and when. Third, we can organize these stories into some structure to give us a simpler view of complex behaviors. Phenomena come in three kinds: - **Individuals**. The individuals are the things that participate in the behavior. Sometimes these are called objects or entities, but I prefer the term 'individual' because it emphasizes the key property (which is to be distinct from other individuals, to have a persistent identity) without getting mired in technology and implementation details. - **Relationships**. Relationships are facts in the world that associate individuals with each other. They can also associate individuals with simple values. There are no observable properties of individuals that are not captured in these relationships; individuals don't have any inherent 'attributes' or qualities that are distinct from the relationships the individuals participate in. - **Actions**. Actions are things that happen in the world. They involve individuals and change relationships. Actions are assumed to be atomic, meaning that they happen instantaneously without any apparent duration. To describe an activity that spans some time, you can define actions that correspond to its start and end. **Example: Restaurant reservations**. To describe the behaviors associated with restaurant reservations, we might have these phenomena: - **Individuals**: `Alice` and `Bob` (who are users); `Maido` and `Rosetta` (which are restaurants); `Slot_13` and `Slot_14` (which are slots available for reservation); `Reservation_3` (which is a reservation). - **Relationships**: `for (Reservation_3, Slot_13)` is the relationship that says that `Reservation_3` is a reservation for `Slot_13`; `by (Reservation_3, Alice)` is the relationship that `Reservation_3` is a reservation by (that is, belonging to) `Alice`; `time (Slot_13, 7:30pm)`, `date (Slot_13, January-10-2026)` and `restaurant (Slot_13, Maido)` are the relationships that say that `Slot_13` is for `7:30pm`, on `January 10, 2026` at `Maido`. - **Actions**: `reserve (Alice, Slot_13):(Reservation_3)` is the action in which `Alice` reserves `Slot_13` resulting in reservation `Reservation_3`; `cancel (Reservation_3)` is the action in which `Alice` cancels `Reservation_3`. **A note about names**: Because `relationship (...)` and `action (...)` look similar, it's easy to confuse them. That's why I've been careful with the names, using simple verbs (like `reserve`) for the actions and non-verbs (like `for`) for relationships. Relationships often associate two things, but they can apply to just one, or to more than two. Generally, however, considering only one-place and two-place relationships is sufficient, and is a helpful simplification. To achieve this, a relationship with more than two places can be replaced by two-place relationships and some additional individuals. **Example: One-place relationships**. The relationship `canceled (Reservation_3)` can represent the fact that `Reservation_3` has been canceled. We might also have relationships such as `for (Reservation_3)` which tells us that the individual `Reservation_3` is a reservation. **Example: Reducing a multi-place relationship**. Suppose you can reserve for someone else, so that the relationship `hasReserved (Alice, Slot_13, Bob)` says that `Alice` has reserved `Slot_13` on behalf of `Bob` who will be the diner claiming the reservation. We can eliminate this three-place relationship by using the individual `Reservation_3` to represent the reservation, so that instead of the one relationship we have three separate relationships: `for (Reservation_3, Slot_13)`, `by (Reservation_3, Alice)`, `diner (Reservation_3, Bob)`. Some phenomena correspond to phenomena in the real world outside the software, but others might be phenomena that belong to the software. The difference isn't always clear, because some phenomena become part of our understanding of the world once the software is in use. **Example: Individuals in the world and in software**. Presumably the users `Alice` and `Bob` and the restaurants `Maido` and `Rosetta` exist in the real world whether there is a software reservation system or not. The individual `Reservation_3` only exists because of the software, but once we're familiar with reservation systems we start to think of reservations as real things. **Example: Relationships in the world and in software**. Likewise, there are relationships that exist in the real world and relationships that exist only in software. Perhaps `Alice` is actually an employee of `Maido`, so `worksFor (Alice, Maido)` . In contrast the relationship `by (Reservation_3, Alice)` which says that `Reservation_3` was made by `Alice` exists only in the software. **Example: Actions in the world and in software**. Likewise, there are actions that exist in the real world and actions that exist only in software. If `worksFor (Alice, Maido)` then presumably at some point `hire (Maido, Alice)` occurred, an action in which `Maido` hired `Alice`. The action `cancel (Reservation_3)`, on the other hand, happens only in the software. Does the software know about all the individuals and relationships? Does it participate in all the actions? Often it's possible to take the simple view that everything that happens is known to the software, and that it stores all relationships. In this view, there is essentially no difference between the real world and the world of the software. **Example: Omniscient software**. In this view, if a relationship like `worksFor (Alice, Maido)` holds, the software must know it. Crudely put, there is a table somewhere in the software that tracks the employees of restaurants (perhaps because the reservation system gives special treatment to employees making reservations at their own restaurants). If the software does not know such a relationship, we shouldn't talk about it. It does not belong to our description of behavior. A more sophisticated view allows some phenomena to be relevant but inaccessible to the software. This view is very helpful for cyber-physical systems, because it lets you consider explicitly the connections between software phenomena (that can be controlled directly) and real world phenomena (that can only be controlled indirectly). **Example: Cyber-physical software phenomena**. In an elevator system, users press buttons and travel in elevators. The software itself sees the button presses but it doesn't see the users. To reason about the design, you might want to say things like 'when `Alice` requests `Floor_12` and is told to take `Elevator_A`, then if she enters `Elevator_A` when it arrives it will take her to `Floor_12`', even though `Alice` is not a participant in any of the actions from the software's perspective. To reason in this way will require combining phenomena that the software knows about (the pressing of buttons) and the phenomena it does not know about (Alice entering the elevator car). Different kinds of users may have different views of the behavior of a software system, seeing only some of the phenomena, or seeing the phenomena aggregated in certain ways. You might want to describe some of these limited views. But designing software is easier if you ensure that the behavior of the software is described richly enough to account for all the different kinds of users. Sometimes this will mean that the behavior seems more complicated than it would need to be for just one kind of user. **Example: Behavior for all kinds of users**. You might have wondered why `Alice` holds a reservation `Reservation_3` of slot `Slot_13` which is for the restaurant `Maido`. Why not just describe the behavior as `Alice` holding reservation `Reservation_3` for the restaurant `Maido` and get rid of `Slot_13`? Such a view would be fine if all the users of the reservation system were diners. But remember that some of the users are the restaurant employees whose job is to tell the system about availability. To describe this, you need slots. You can see the essential difference between slots and reservations when you consider how party size is handled. You could define relationships such as `minParty (Slot_13, 3)` and `maxParty (Slot_13, 6)` (which say that Slot_13 is suitable for a party of 3-6 people), and then a relationship `partySize (Reservation_3, 5)` (which says that `Reservation_3` is for a party of 5 people). Note that the maximum and minimum are associated with the available slot; the actual party size is associated with the reservation. The relationships form the basis of the view that users have of the running software system, but they don't necessarily see the relationships directly. Instead, what they see will be a view that is formed by combining and filtering relationships. There are also actions that users don't see, even if they are relevant to them. **Example: User view extracted from relationships**. The reservation that `Alice` holds is associated with `Slot_13`, which will determine what table she gets seated at when she arrives. But of course in the view that the software shows Alice she will see only that she has a reservation for a table at `Maido` at `7:30pm` on `January 10, 2026`. **Example: Action not visible to user**. If `Alice` does not turn up at the restaurant at the expected time, the action `noShow (Reservation_3)` may occur. She likely won't be notified of this, even though (as the holder of that reservation) she is the implicit subject of this action. If she fails to turn up repeatedly, the multiple `noShow` actions will eventually cause her account to be disabled and prevent her from making reservations. The phenomena are like the vocabulary for the language of behavior. When you describe behavior, you have to choose this vocabulary carefully. Generally, it will not correspond directly to the steps that are immediately apparent to a user of the software. Instead, the phenomena are *interpretations* that capture the understanding of the behavior that users and the designer alike hold in their minds as the simplest way to think about the software. In particular, actions taken by the user usually correspond to coherent requests at a higher level than the small gestures the user might need to perform the create those requests in a user interface. **Example: High level actions**. The action `cancel (Reservation_3)` represents a reservation being canceled. A user may execute this action by clicking on a link in an email message, which then opens the user's browser at a page associated with the reservation, inviting the user to then click on a button to confirm the cancellation. This long and elaborate sequence of steps is abstracted into a single, simple action that is deemed to occur at the moment at which the final button is pressed (or when the HTTP request has been processed by the server). A *trace* is a sequence of actions, a story of what happened. At each point in the trace, from before the first action until after the last, there's a set of relationships that hold there. Together the trace and its relationships comprise a *behavior*. **Example: A behavior**. Here is a trace that includes some relationships after one of the actions (and is therefore only a partial behavior): ``` createSlot (Maido, 6:00pm, January-10-2026): (Slot_1) createSlot (Maido, 6:00pm, January-10-2026): (Slot_2) createSlot (Maido, 7:30pm, January-10-2026): (Slot_3) reserve (Alice, Slot_3): (Reservation_1) by (Reservation_1, Alice) for (Reservation_1, Slot_3) restaurant (Slot_3, Maido) time (Slot_3, January-10-2026) date (Slot_3, 7:30pm) reserve (Bob, Slot_2): (Reservation_2) cancel (Reservation_2) redeem (Reservation_1) ``` The story this trace tells is that three slots were created for Maido; Alice then reserved one of those slots; Bob reserved another slot, and then canceled it; Alice then turned up and redeemed her reservation (and was seated). The relationships after the `reserve` action tell us that the resulting reservation was by Alice, and for a slot at a given restaurant on a given day and time. Not even all the relationships that hold at that point are included; for example, `time (Slot_2, 6:00pm)` holds too because even though the slot has not been reserved it exists with its start time. Equally (sometimes more) important in understanding behavior is to know what does *not* happen. In fact, one way to define the behavior of a system is to specify all the traces that are forbidden; the traces that are left are then those that should be allowed. **Example: A non-behavior**. Here is a trace that is forbidden because `Alice` and `Bob` have reserved the same slot: ``` createSlot (Maido, 6:00pm, January-10-2026): (Slot_1) createSlot (Maido, 6:00pm, January-10-2026): (Slot_2) createSlot (Maido, 7:30pm, January-10-2026): (Slot_3) reserve (Alice, Slot_3): (Reservation_1) reserve (Bob, Slot_3): (Reservation_2) ``` How then to specify all the allowed behaviors, or even all the forbidden ones? The problem is that since the number of individuals is generally unbounded, there are infinitely many traces. To do this, we're going to need to find some regularity, so that we can express some general rules. So far we've talked only about particular individuals, relationships and actions. But these can be grouped into sets that capture their commonalities. The sets that individuals belong to are their *types*. The sets that relationships belong to are called *relations*; a single relation is a collection of distinct relationships (sometimes also called *tuples*). If a relationship is a row, a relation is a table. The set that actions belong to are classes or types of action; there's no good word for this, so it's common call the set an *action* too, and when you want to distinguish an instance from the set, calling it an action *occurrence*. **Example: Particulars and their sets**. The individuals `Alice` and `Bob` are members of the type `User` of all possible users; the relationship `by (Reservation_3, Alice)` is a member (or tuple) of the relation `by`; the action occurrence `cancel (Reservation_3)` is an instance of the action `cancel`. Describing all behaviors over all possible individuals is obviously impossible. Fortunately though there are regularities that can be captured, and by expressing these (as rules) you can implicitly define all the behaviors even if there are infinitely many of them! There are two kinds of regularity. The first constrains the sets or types. You can say that a relation always relates individuals drawn from certain types, and similarly for the individuals that an action involves. **Example: Typing actions**. The action `cancel` always involves a single individual that is a reservation. You can write this as `cancel (reservation: Reservation)`, which says that every occurrence of a cancel action involves an individual of the type Reservation (which is given the placeholder name `reservation`). The type of the `reserve` action is `reserve (user: User, slot: Slot): (reservation: Reservation)` , which says that it takes a user and a slot and produces a reservation. **Example: Typing relations**. The notation for typing relations is more elaborate, since it is designed to make it easy to group together all the relations associated with a given individual type. For example, this declaration ``` a set of Reservations with an by User an for Slot ``` introduces two relations, `by` which associates a reservation with a user, and `for`, which associates a reservation with a slot. Something subtle: it also introduces a one-place relation (that is, a set) called `Reservations` representing the set of known reservation individuals. **Note: Naming relations in practice**. In practice, it's common to give relations names that don't refer to the role of the individuals involved but just refer to their type. For example, we might declare ``` a set of Reservations with a user User a slot Slot ``` introducing the names `user` and `slot` for the two relations, and then we might write ``` Reservation_3.user = Alice ``` to say that the user associated with `Reservation_3` is `Alice` (or we might just say exactly that using informal English!). Note that we have actually already introduced relation names of this kind, with `time`, `date` and `restaurant` all named by the individuals involved and implicitly assuming the meaning (eg, that the time of a slot was its start time, and not its end time). The second kind of regularity in behavior is how actions change relations. Whether an action can occur may depend on whether certain relationships hold, and the effect of it occurring is to add new relationships, remove old ones (and leave other relationships in place). To describe this, you can give a spec for an action that has three parts: the *signature* (listing the *arguments*, namely the individuals that participate and their types), the precondition (saying which relationships must hold for the action to happen), and the postcondition (saying which relationships must hold after the action has happened). The keyword `requires` labels the precondition, because it says what is required for the action to happen, and the keyword `ensures` labels the postcondition because it says what the action ensures will be true after. Note that in the precondition, which relationships are required usually depends on the action's arguments; and in the postcondition, which relationships are ensured usually depends both on the arguments and on which relationships were present before. **Example: Specifying an action**. ``` reserve (user: User, slot: Slot): (reservation: Reservation) requires slot is not already reserved ensures a new reservation is created that is for slot and by user ``` The spec is written informally. Put more formally, the precondition says that there is no relationship before the action happens of the form `for (reservation, slot)` where `reservation` is any reservation and `slot` is the particular slot being reserved here. The postcondition says informally that relationships that look like this will hold after: ``` for (reservation, slot) by (reservation, user) ``` where reservation is the particular reservation that has been created, and slot and user are the given arguments. So, in this case, when the action happens, a new individual appears (the reservation) and two new relationships are added (associating it with two existing individuals). # How to structure the behavior (functionality) of a software system When you build software, you figure out what behavior you want and then you implement the behavior. With concept design, you define the behavior in terms of observable phenomena in the world: *individuals* that participate in *actions* that result in *relationships*. By building the software around these phenomena, you maintain a strong connection between the software and the real world, and ensure that it is *legible*: that what you see is what it does (that is, the code describes behaviors directly). Having defined behaviors isn't enough, however. You need to organize those behaviors: to structure the system so that different parts of it manage different parts of the behaviors. This is the classic idea of *modularity*. You break the system into pieces so that each piece can be worked on independently, and then you bring the pieces together to form the system as a whole. Organizing the system into parts has another important benefit. Psychologists use the term *chunking* for the way in which we recognize and understand ideas in 'chunks' rather than in terms of their smallest elements. A novice piano player starts by reading notes one at a time. But a musician thinks of a musical composition not as a sequence of notes, but in increasingly larger chunks: bars, motifs, chord progressions, melodies, themes, and so on. Viewed in terms of chunks, the size and complexity of the composition shrinks, and it becomes possible for a performer to hold the entire composition in their head and play it without referring to the score. The same advantages are possible with software design. By organizing the behavior in chunks, you can recognize familiar patterns and make it easier to understand and talk about a complex system. **Example: Chunking for restaurant reservations**. To grasp the main ideas in a restaurant reservation system, few people would need the behavioral details. You could instead tell them what the chunks are: (a) registering and authenticating users; (b) planning availability of dining slots; (c) letting diners reserve slots; (d) confirming reservations; and so on. Each of these chunks involves a collection of phenomena and behaviors that the words evoke and that most people are familiar with. Ideally, the mental chunks and the software modules will be the same. Both structure the behavior into manageable pieces, and allow it to be implemented directly. So the question is: what should these chunks or modules be? In the software world, there's one dominant answer to this question. It's not always explicit but it's often hiding in the background. That answer comes from object-oriented design, and suggests building units of functionality around the individuals. You take the type of some individual, and then you associate with it all the functionality associated with the individuals of that type. **Example: Object-oriented chunking for restaurant reservations**. In a restaurant reservation system, you'd take individual types such as `User`, `Reservation` and `Restaurant` and make them into objects, each implemented as a class that contains functionality associated with those individuals. So reserving and canceling would belong to `Reservation`, registering and authenticating to `User`, assigning available slots to `Restaurant`, and so on. The combination of an individual (a persistent identity), the relationships about that individual, and the actions that involve that individual, form an *object*, with the relationships often called *fields* or *instance variables*, and the actions called *methods*. In most object oriented approaches, all the individuals of a given type are assumed to have the same types of relationships and actions, giving the notion of a *class*, which then defines a set of objects with given field and method types. This approach is appealing for its seeming simplicity, with the individuals themselves as both *participants* and *locations* of functionality. It works well when functionality really is naturally located at an individual. That was arguably true for simulations, the domain that inspired the pioneering work in object-orientation (leading to the appropriately named Simula language). But for most software designs it doesn't work so well. The problem is that almost all behavior involves more than one individual---either individuals of different types, or individuals of the same type. In this situation there is no natural way to assign functionality to objects. Which object a relationship or action belongs to is usually unclear. A crucial clue that this scheme won't work is that almost all relationships and actions involve *more than one individual*. Deciding which individual a relationship or action should be assigned to becomes arbitrary, and the result is that functionality tends not to be organized in a rational way. It usually ends up both fragmented --- meaning that functionality that belongs in one place gets split between two or more -- and conflated --- meaning that different aspects of functionality that don't belong together end up in the same place. **Example: An action in a restaurant reservation system that involves exactly one individual**. Take the action `cancel (Reservation_3)` in which a particular reservation (`Reservation_3`) is canceled. This is the happy case for object-oriented design. The action is about a single individual; it doesn't need to involve any other individuals (such as who the reservation is for, or which restaurant it's at) since those individuals are implicitly associated with the reservation anyway. To cancel a reservation, the system just needs to know which reservation is being canceled. In an object-oriented design, this action can quite reasonably be regarded as a method of the reservation object. **Example: An action in a restaurant reservation system that involves more than one individual**. Unfortunately such actions are rare. The quintessential restaurant reservation action is the one in which a reservation is made. Consider the action `reserve (Alice, Slot_13):(Reservation_3)` in which Alice reserves `Slot_13` (corresponding to some table in some restaurant at a particular time), creating the reservation `Reservation_3)`. Which individual does this action belong to? The user `Alice`, because she's the one making the reservation? The slot `Slot_13` because it's the slot being reserved? The resulting reservation? There's no good answer here. (Experienced object-oriented programmers would note that one choice is ruled out: it can't be a method of a reservation object since no reservation exists prior to the method call! The fact that this action creates a reservation will actually require that it be associated with the reservation class, but as a constructor and not a method.) **Example: An action in a restaurant reservation system that involves more than one individual**. Another example: the action `upvotes (Alice, Maido)` in which the user `Alice` upvotes or likes the restaurant `Maido`. Does this action belong to the user or the restaurant? Of course the question is a silly one: the action is no more about one than the other. And if we were to lean towards the user as the instigator of the action, we'd end up having to assign almost every action to a user! **Example: A relationship in a restaurant reservation system that involves more than one individual**. Consider the relationship `by (Reservation_3, Alice)`, which records the fact that `Reservation_3` is a reservation belonging to `Alice`. Which individual does this belong to, the user or the reservation? Again, the question is impossible to answer in behavioral terms. A relationship between two individuals is just that -- a relationship -- and it belongs to neither. An object-oriented programmer would typically assign the relationship according to the direction of expected navigations. So since we're expecting to have to query a reservation to find out who it's for, we assign the relationship to a field of the reservation object, so you can write `reservation.by`, for example, to find out who the reservation was made by. But now that means we can't query a user to find out what reservations they have! So we'll put it in the user object too, and write `user.reservations`, for example, to find all the reservations for a given user. Now we're in a real mess, with the same relationship expressed redundantly in two different places. Many actions point to some individuals as their participants, as arguments of the action, but the behavior associated with the action involves a larger collection of individuals that are not explicitly referenced. A particularly common case arises when an action that might seem to be about a single individual is actually about the entire set of individuals of that type. An object-oriented approach will typically deal with this using a workaround, by introducing a new individual representing the set, but this is counterintuitive and undermines the basic premise that organizing functionality does not require introducing new structural components. **Example: Set objects for user authentication**. Consider the action `login('alice', 'foo'): Alice` in which a user enters the username `alice` and password `foo` to authenticate as the user individual `Alice`. Presumably, in an object oriented design, there will be an object corresponding to `Alice` that has username and password fields. But the `login` action can't be a method of this object, because the very function of the action is to find that object, by matching the username fields of objects to the given username in the action. The traditional solution in object-oriented design is to introduce a new class that contains a single object that has a field that contains the set of all user objects. This class might be named `Users`, each of whose instances represents a set of users, distinct from the class `User`, whose instances represent individual users. Concept design takes a simpler approach. You break the system into concepts, each corresponding to a complete and coherent aspect of the functionality. Complete means that it makes sense by itself; coherent means that it doesn't mingle unrelated aspects. Each concept is generally associated with some ongoing activity, and serves a definable purpose. **Example: Restaurant reservation concepts**. The concepts of a restaurant reservation system might include `Reserving` (the functionality associated with reserving available slots); `Availability` (the functionality associated with setting availability, determining how many slots will be available and for what party sizes); `Authenticating` (the functionality associated with authenticating users); `Notifying` (functionality associated with sending users notifications, for example confirming reservations); and so on. You then assign each action and relationship to a single concept. This is much easier than assigning to objects. To be clear, what gets assigned here is the *type* of the action or relationship, so strictly speaking we should be talking about assigning *relations*, not relationships. (We don't have a separate type for action types.) **Example: Assigning restaurant reservation actions and relationships to concepts**. For example, the `reserve` and `cancel` actions get put in the `Reserving` concept; the `register` and `login` actions go in the `Authenticating` concept; the `notify` action goes in the `Notifying` concept; etc. The relation `by` goes in `Reserving`, the relation `hasPassword` (which associates a user with their password) goes in `Authenticating`, and the relation `hasSMS` (which associates a user with their SMS texting number) goes in `Notifying`. Some actions may seem to be relevant to multiple concepts. This is possible when something that happens in the world that itself has different aspects of behavior. In this case, you can define additional actions to represent the other aspects of behavior, so that each action is still assigned to just one concept. **Example: A restaurant reservation action that seems relevant to two concepts**. Consider what happens when a user fails to turn up for their reservation. Until now, we've assumed this is represented by an action such as `noShow (Reservation_3)`, which is relevant to the `Reserving` concept, because it establishes the final outcome of the reservation. At the same time, it seems as if the repercussions for the offending diner belong to some other aspect of functionality. So you might add a concept called `Karma`, say, whose functionality is to track the behavior of users, with an action `misbehave` that occurs whenever a user misbehaves. Now when `noShow (Reservation_3)` happens (in the `Reserving` concept), `misbehave (Alice, Reservation_3)` can happen too (in the `Karma` concept), to represent the fact that `Alice` misbehaved with respect to `Reservation_3`. At this point, you may be wondering what's novel about concepts. Aren't they just regular software modules that divide functionality up without the pitfalls of object orientation? No, they're not. There are two fundamental respects in which concepts are special. First, concepts never 'call' each other. In conventional designs, different pieces of functionality are connected by procedure calls from one to another. By avoiding this, concept design lets you keep talking at the level of behavior, without the complication that the effect of an action may be to do some sequence of steps that include calling other actions. Also, not having calls ensures that concepts remain fully independent of one another. Of course when concepts are brought together into a system, it will be necessary to coordinate them so that when certain actions happen in one concept, other actions happen in another. This is done using *synchronizations* (or syncs): rules that define action causation links. The key point is that these synchronizations are outside the concepts: the coordination is *externalized*. **Example: Actions not calling each other**. In a conventional design, the `noShow` action of the `Reserving` module might call the `misbehave` action of the `Karma` concept. That is, when the `Reserving` module is told that a reservation is a no show, it tells the `Karma` module to punish the user. In a concept design, there is no such call. Instead, you'd add a synchronization that says that *when* `noShow` happens for a given reservation, *then* `misbehave` happens for the reservation and its user. Second, concept design takes what would be the state of a single object in a conventional system and breaks it across multiple concepts. This is what allows concept design to cleanly disentangle the different aspects of functionality. **Example: Breaking object state across concepts**. In a conventional design of our restaurant reservation system, there would likely be a `User` class that has a `password` field that holds the user's password and a `points` field that holds the number of karma points the user has (which may be negative if the user has misbehaved). In a concept design, these fields represent relationships that belong to different aspects of functionality. The `password` field is for authenticating; the `points` field is for tracking karma. So the `Authenticating` concept would store, in its relations, a mapping from user identifiers to their passwords, and the `Karma` concept would store a mapping from user identifiers to their points. In summary, concept design organizes a system around concepts, which are complete and coherent aspects of functionality, grouping together behavioral phenomena. Each action and each relation is assigned to exactly one concept. But what about the third kind of phenomenon, the individual? Unlike the actions and relations, the individuals are cross cutting, the same individual participating in behaviors in multiple concepts. Later, we'll see that the theory of concept design is a bit more subtle than this suggests, and that each type of individual will indeed be associated with a single concept, as its primary home. # How concepts describe domain behaviors and services at once Concept design divides up the description of the behavior of a software system into independent parts. Each part is a concept that describes an aspect of the behavior in terms of its own relationships and actions. But a concept is not only a description of behavior. It can also be understood as a specification for a service that can be constructed to provide that behavior. Each action type of the concept becomes an API call of the service; each relationship type (aka relation) becomes a state component of the service. When the service is called, it updates its state according to the behavioral rules that dictate how actions affect relations. **Example: Restaurant reservation service**. The `Reserving` concept of a restaurant reservation system define behaviors associated with reservations, in particular that there are relationships `for` and `by` that relate reservations to slots and users respectively, and actions such as `reserve` and `cancel` that affect these relationships. This same description can be viewed as a specification for a reservation service that stores in its state a set of reservations with the slots and users they are for, and that offers commands `reserve` and `cancel` that update these state components. # An introduction to concept design ## What concept design is Concept design is a tool for people who create software, whatever their role may be. For software engineers, architects and programmers, concept design offers a way to structure complex functions and move easily to clean and maintainable code. For product managers and user experience designers, concept design offers a way to express and analyze user interactions at a high level. For CTOs, chief architects and VPs of engineering, concept design offers visibility into complex projects and a way to focus effort and foster collaboration across teams. For sales, marketing and legal people, concept design offers a shorter path to grasping the key features of each product and the value that it brings. Concept design helps you make software better, and make better software. Making software better means capitalizing more effectively on prior knowledge and experience, communicating ideas with greater clarity and succinctness, and enabling a smooth path to implementation. Making better software means aligning functionality with the needs of users and providing a compelling and simple mental model. Software should be not just usable but a delight to use. This is achieved not by adding frills but by eliminating the friction that frustrates users and that limits the product's market potential. ## Achieving Simplicity As Tony Hoare put it, there are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. It may seem naive to imagine that a simple design can deliver rich functionality in a complex domain. But, as Hoare realized, there is no viable alternative, because the cost of complexity is unsustainable. Simplicity can be achieved by a combination of technique and attitude. The technique involves means of breaking a complex system down into simple parts, so that even if the aggregate behavior is extremely complex, the parts can be easily understood. The attitude requires an unrelenting insistence on the utmost clarity, and a willingness to overcome a tolerance for incomplete understanding. ## A Mindset Shift Concept design calls for a shift in mindset in three key respects. - **A new language of discourse**. To describe software functionality, concept design offers a language of behavior in which *actions* that involve *individuals* cause changes in *relationships*. Software designs are more conventionally described either in terms of user interfaces (with wireframes and user journeys that involve clicking buttons and navigating through screens) or in terms of implementation (with code components, or objects and classes that have no correspondence to the problem domain). By adopting ideas from data modeling, process algebras and model-based specification, concept design allows functionality to be described in a succinct and understandable way that is independent of user interfaces or implementation. - **A new way to structure function**. Much of the history of software engineering involves attempts to achieve better separation of concerns and modularity. Concept design provides a new approach that builds on two well-established ideas: event-based architectures and view separation. Using event synchronization as the sole form of composition, concept design allows functionality to be broken into modules that have no mutual dependencies whatsoever (since all connections are externalized with event mediators). Using view separation to disentangle the behaviors associated with individuals, concept design allows modules to be more cohesive and avoids the conflation of different aspects that should be kept separate. In these two moves, concept design follows other techniques (such as entity-component structuring, subject-oriented programming, aspect-oriented programming and data-context-interaction) that have sought to overcome the limitations of object-oriented approaches. - **A new emphasis on purpose**. Designers have long recognized how helpful it is to ask not only *what* a product should be but also *why*. Concept design challenges designers to find motivations not just for the product as a whole but for each individual concept, asking what would go wrong if it were omitted, and what purpose it serves. ## Reuse A key goal of the structuring in concept design is to expose more opportunities for reuse. In standard approaches, application-specific details prevent it. For example, if you build a domain model in an object-oriented way, you're likely to end up with objects that have features that are not shared with other apps. To eliminate these anomalies but preserve the behavior of the app as a whole, you need a structure that can cleanly separate general-purpose aspects of behavior from application-specific ones. Once you've done this, you can identify reusable design elements. Often you can even recast the overall app design as a (maybe small) collection of extensions of completely standard concepts, which focuses on what's novel and reduces risk. ## Alignment One of the biggest challenges in software development is that people in different roles speak different technical languages. On the UX side, people communicate with wireframes and user flows, but on the engineering side, they draw box-and-line diagrams such as entity-relationship and component/interface models. Concept design offers a language that can be shared across roles, because it addresses the fundamental shared concern (of what the behavior of the product is, and why) without infringing on the specialized concerns that are relevant only to some roles (namely the user interface details that are not relevant to engineering, and the architectural details that are not relevant to UX). In addition to alignment across roles, concepts also serve to align across teams. Teams need to be able to work independently, but it's become increasingly important to align designs around shared concepts, because of cloud integration and because of users expectations that the same functionality will work in the same way in all the products in a product suite. ## Writing designs Writing is an essential part of design. Articulating design ideas clearly and succinctly is hard work, but has big payoffs in clarifying options, aligning teams and exposing potential risks. For the last two decades, design writing has been discouraged, especially by the proponents of "agile development" who valued only deliverable code. Now, the advent of LLMs is turning the tide back, as it becomes clear that the key assets of a development will increasingly be design documents, because they will be essential for leveraging LLMs in design analysis and code generation tasks. # An introduction to concept design method Concept design has the following steps: - Identifying the concepts that will comprise the functionality - Adjusting the boundaries between concepts - Defining the concepts in detail - Defining the synchronizations between the concepts Once these steps have been completed, the design can proceed to implementation. Of course development will usually be incremental with design and implementation interleaved. Concepts provide a good granularity for incremental development because of their independence, and because each concept brings an identifiable additional value. We'll now look at each of these steps more closely. ## Identifying concepts There are two ways one might go about identifying concepts that sit on opposite ends of a spectrum. One way is to define the required behavior of the software (that is, the intended functionality) in detail, identifying types of individuals, relationships and actions and then defining the behavior by considering for each action which individual types are involved and what effects the action has on relationships. This might be termed a *bottom-up* approach. **Example: Punishing misbehaving users in restaurant reservation system**. In designing a restaurant reservation system, you might consider the `noShow` action in which a diner fails to show up for a reservation. As you think about how to handle this, you might decide that no-shows should be counted, and that after some number of no-shows, the user should not be permitted to make reservations. You then might think about whether this behavior belongs and realizing that it is somewhat orthogonal to the functionality of reserving, you begin to think about a concept that tracks how well users behave. You then might wonder what other behaviors might be included, and whether users should be punished for canceling reservations too often. All of these considerations eventually lead you to a behavior-tracking concept. Another way is to define the required behavior not in terms of its detailed phenomena, but rather by picking familiar concepts that are matched to the desired functionality. This might be termed a *top-down* approach. **Example: Karma in restaurant reservation system**. In designing your restaurant reservation system, you might go through all the conventional concepts of social media apps wondering which might apply. One of the concepts you consider is Karma, in which users accrue points for good behavior (used for example in Hacker News to grant certain privileges such as the ability to downvote posts). You figure out that this concept might indeed be useful, because you could use to discourage anti-social behavior such as no-shows and frequent cancellations. In practice, some combination of the two approaches is most effective. The advantage of the bottom-up approach is that it pays attention to details of behavior that might turn out to be important. The advantage of the top-down approach is that it encourages reuse and discourages reinventing the wheel, elaborating behaviors that are well understood. Further, the top-down approach exploits psychological 'chunking': the designer is thinking at a higher level (in terms of entire concepts) and is therefore getting more intellectual leverage. Another advantage of the top-down approach is that consideration of existing, known concepts may suggest functionality that the designer had not considered, so in this respect the design activity can be more innovative, and less tied to initial presumptions about the intended behavior. On the other hand, using only existing concepts may tend to make a design less innovative (although finding new ways to assemble existing ideas is a form of creativity, described by Margaret Boden as *combinational creativity*). **Example: Combining top-down and bottom-up**. Starting bottom-up, you might wonder how to handle the `noShow` action. Thinking about the functionality you want, in which the user is punished for anti-social behavior, you realize that the standard `Karma` concept has exactly this purpose. So you attempt to match that concept to the domain of restaurant reservations, and examine the respects in which it addresses this and other concerns. ## Adjusting boundaries between concepts After you have identified some key concepts, it may still not be clear how functionality is to be divided up amongst them and what exactly the boundaries should be between them. To resolve these questions, concept design offers some design criteria that distinguish good from bad concepts: - **Clarity of purpose**. A concept should have a clear and compelling purpose that delivers identifiable value to users. - **Completeness**. A concept should deliver a complete unit of functionality and not just some part of it. The common reason for failing to achieve completeness is that functionality is fragmented across concepts. Thus *completeness* and *fragmentation* are opposites. - **Specificity**. A concept should deliver a coherent aspect of functionality, and should not mix in other aspects of functional that are not essential to it. The common reason for failing to achieve specificity is that different aspects of functionality are conflated within the concept. Thus *specificity* and *conflation* are opposites. These criteria are related: completeness means fulfilling the purpose in its entirety; specificity means fulfilling only the purpose, and not some other purpose as well. **Example: Completeness criterion for a user authentication concept**. Suppose you are designing a concept for authenticating users, and your initial plan is to have two concepts, one that handles creation and deletion of user accounts, and another than handles the checking of passwords. These concepts would be incomplete, because they fragment the functionality of user authentication. The activity of user authentication involves the setting up of a password and then the entry and checking of that password at authentication time. Separating these into two concepts makes no sense, because they are so closely coupled. A change in whether passwords are hashed and salted for example could not be made in one without affecting the other. **Example: Specificity criterion for a user authentication concept**. Suppose you are designing a concept for authenticating users, and your initial plan is to include in the concept state not only the username and password of the user, but also the name of the user that is displayed publicly (in social media posts, for example), as well as the user's email address, which you expect to use to notify them when certian events of interest occur. This concept design would fail the specificity criterion, because it conflates with user authentication aspects of behavior that don't belong. The display name is not part of the authentication process, nor is the email address (since its intended use is notification and not authentication). Checking that the concept has a clear purpose might expose these design flaws too, as you might realize that you'd have to change the purpose to something like 'managing users' to accommodate this broader functionality beyond authentication, which is not a clear or compelling purpose. ## Defining concepts in detail Two criteria are particularly important in designing the details of a concept's behavior: - **Generality**. A concept should, whenever possible, be generic and reusable in other contexts. You should therefore examine any proposed concept and consider whether it contains elements that might compromise its generality. - **Familiarity**. Other concerns aside, a familiar concept is always preferable to an unfamiliar one. Users are more able to use familiar concepts than learn new ones. Familiar concepts have lower development risks (since their quirks and liabilities are well understood), and LLMs can usually generate code for them particularly effectively due to their presence in training material. **Example: Generality for a photo cataloging app**. In designing an app for cataloging photos, you are considering a concept that allows the user to select a set of photos and delete them in aggregate. But when you think more about this, you realize that selecting photos is a concept in its own right, that can be factored out to allow other operations to be applied in aggregate. Moreover, nothing about this `Selecting` concept is photo-specific, so the same concept could be applied to other items such as movies. **Example: Familiarity for a restaurant reservation concept**. You are designing a restaurant reservation system, and your manager suggests a feedback concept in which, after a diner has eaten at a restaurant, they received a request for feedback, and the reservation system samples the feedback that is provided and generates a summary that is displayed publicly. You warn your manager that this concept, while it might turn out to be effective, will be unfamiliar to users, and they are likely to be confused about whether their feedback is private or public. Instead, you recommend providing familiar concepts, such as a private feedback concept for complaints, and a public rating concept for evaluations. The generality criterion should be applied with caution. Often a more specialized concept is appropriate. **Example: Over-zealous generalization of a restaurant reservation concept**. You are designing the `Reserving` concept that handles reservations of dining slots. You have realized that some of the properties of the slots themselves, notably which tables they are associated with, can be factored out into a separate concept. You are encouraged in thinking this way by the recognition that the process of configuring tables in a restaurant is an elaborate one that almost certainly deserves its own concept. But you get a little carried away, and thinking about your Reserving concept, you are eager to make it fully general so that it might apply not only to reserving dining slots but also other things -- airline seats, haircuts, conference rooms, and so on. In that case, you reason, the `Reserving` concept should not associate party sizes with reservations, because that is restaurant specific. This would be a mistake, since the party size is essential to the functionality (typically a restaurant needs to know how many places to set, and may not seat the party until everyone has arrived), and factoring it out into its own concept would be overkill. ## Defining synchronizations between concepts When considering how to synchronize concepts, the following list of common motivations for synchronizations may be helpful: - **Invariant preservation**. Most state invariants are preserved within individual concepts, but there are likely to be system-level invariants that cross concepts. To maintain these, an action that updates a state component in one concept will need to be sync'd with an action that updates the corresponding state component in another concept. - **Authentication and authorization**. To ensure that only certain users can access certain functions and data, the users need to be authenticated (to determine *who* they are) and then authorized (to determine *what* they can do). - **Notification**. To keep users informed about action occurrences that are relevant to them, it is common to generate notifications that provide users with updates in their channels of choice. - **Default actions**. To reduce the burden on users, it is common to sync some actions so that they happen by default without the user having to invoke them explicitly. - **Logging**. For reasons such as auditing, preserving legal records, analytics by system administrators and so on, it is often important to generate logs of action occurrences. A generic logging facility for all actions can be programmed as part of the concept architectural framework and need not be part of the application design, but there may be a need for domain-specific logging that handles certain actions in a special way. Here are examples of each of these: - **Invariant preservation**. Cascading delete: when a social media post is deleted in a `Posting` concept, for example, all the comments targeted at that post in a `Commenting` concept should be deleted too. This preserves the invariant that if a comment exists, then its target exists too. Another example: a `Labeling` concept assigns the label deleted to emails that have been deleted in the `Trash` concept; the deleting and restoring of emails in the trash will have to be sync'd with adding and removing the deleted label. Another example: when a user chooses an email address to be used as a from-address, the outgoing SMTP server that matches the domain of that email address should be chosen (to preserve the invariant that the from-address domain matches the domain of the sender of an email; if not, the message is likely to be marked as spam). - **Authentication and authorization**. Multiple concepts are likely to be involved here: `UserAuthentication` to authenticate users; `Sessioning` to maintain user identity over sessions; `RoleBasedAccessControl` to simplify assigning permissions to sets of users; `Permissioning` to manage permissions associated with resources; and so on. Typical syncs will cause multiple checks to be performed before an action on a resource is allowed. Note that authentication is often used in a sync to assign an owner to a resource: for example, an action to create a post in a `Posting` concept may take a user argument which is provided by a sync that obtains the user from the current session. - **Notification**. Typical syncs will include not only the sync that generates a notification, but also the syncs that subscribe the user to particular notifications. For example, when a user creates a post, they may be subscribed to notifications for comments on the post. - **Default actions**. When a user responds to an email message from a sender, they would expect that sender to be automatically whitelisted. When a user schedules a video meeting for a particular date, it should be added to their calendar automatically. - **Logging**. An example of domain-specific logging: a credit card system should log declined payments in the list of transactions so card owners know when an attempt to use their card was made, and ideally also why it was declined. Synchronizations offer a form of automation. Too little synchronization means that there is too little automation and users are forced to take explicit actions when they would rather them be done on their behalf. Too much synchronization means that there is too much automation and users lose the agency of being able to control whether an action happens. **Example: Under-synchronization**. The `lowerHand` action is Zoom is notoriously under-synchronized. Users often forget to lower their hands, which other participants mistakenly interpret as a new request to talk. In the current design it is not clear what action to synchronize with. If there were a concept that governed the user's participation mode (listening, speaking, requesting to speak), `lowerHand` could be synchronized with `startSpeaking`, for example. **Example: Over-synchronization**. A notorious example of over-synchronization: in versions of both Google's and Apple's Calendar app deleting a calendar event would be synchronized with declining any invitation associated with it, or even with canceling the event. In the latest versions, the user is given the option to delete without declining or canceling. # A rubric for evaluating concept specifications This document lists common flaws in concept definitions. It is intended to be used as a rubric for checking definitions, and should be extended as new ones are identified. **Flaws in concept boundaries**. These flaws address the way in which functionality is divided up amongst concepts. - **Conflation**. The concept conflates two different aspects of functionality, that could be separated into two concepts without causing fragmentation. Example: a concept includes both user authentication and user profiling. Often conflation is the result of an object-oriented point of view in which all functionality associated with some object type is mistakenly put in a single concept. Example: a concept that manages information about users that includes not only authentication but also karma points that are accrued as a measure of good behavior (and are completely unrelated to authentication and easily separated from it). Sometimes conflation is more subtle and subjective, and simplicity may argue for a single concept but a more rigorous separation of concerns for two. Example: authentication of users and session management can be easily separated into two concepts, and doing so allows each to be reused without the other (authentication of special requests without sessions, for example) and prevents coupling of design decisions (the use of passwords for authentication and the use of session tokens for sessions, which should be independent). Nevertheless, a concept that includes both authentication and sessioning (with actions to register for an account, then login and logout) is simpler to understand and not unreasonable. - **Fragmentation**. A single aspect of functionality is split across two concepts, resulting in needless duplication of state and excessive synchronization. Example: the functionality of a shopping cart is split into a concept that handles additions of items to the cart, and a separate concept that manages checkout. A cause of this is a misunderstanding that different temporal phases in functionality deserve different concepts. Example: registering for an account is a distinct activity phase from logging in, but both are part of authentication and share essential state (usernames and passwords) so cannot be profitably separated. Sometimes this is plausible, when the phases are very distinct and involve different kinds of users, and may be separately reused. Example: separate concepts for creating surveys and collecting user responses can make sense, because the activities are elaborate in their own right, independently reusable (a concept for creating and editing surveys could have a pedagogical use even without answers being collected, and a concept for collecting answers could be used for different kinds of surveys with differences in how questions are structured). **Violations of modularity**. These flaws address egregious violations of the principles of modularity in concept design. - **Data dependence on another concept**. The concept may imply (for example in the definition of an action) that a property of an external individual that is defined in another concept is dereferenced by this concept. This misunderstands the idea of individuals in concept design, which are always represented only by their identities, and associated with properties by the states of each concept, with those properties never accessible outside. Novices may make this mistake because they think of individuals as composite objects with fields that can be accessed. Example: a user authentication concept defines a state that assigns a username to each user; a concept that manages social media posts defines a state that assigns a user as an author to a post; the specification of the posting concept erroneously refers to the username of the author, which is not accessible in that concept. - **Function calls to another concept**. Concepts never have calls from the internals of one concept to the interface of another; all coordination is external, with synchronizations defining how actions of one concept are coordinated with actions of another. This is the only way that data can flow from one concept to another, and the only way that an action occurrence in one concept can cause an action occurrence in another. Example: a user authentication concept mentions that the effect of registering for an account includes notifying the user using a notifying concept. This could be specified as a sync associated with the concept, but any intimation that there is a direct call (for example mentioning such a call in the specification of the registering action) is mistaken and violates concept independence. - **Reference to the system or to other concepts**. Concepts should be independent and standalone, so that they can be reused in different contexts. It is therefore a mistake to reference the role that the concept plays in the larger system or the existence of other concepts. This is especially true in the definitions of states and actions. Example: in a user authentication concept intended for use in a social media app, the purpose should be to authenticate users and not to 'authenticate users when the create or edit posts'; the action for registering should not say something like 'registers a user of the social media app'. There are three important exceptions to this rule. First, the notes section of a concept is expected to include information about the role of the concept in the system and its relationship to other concepts (although preferably such notes should be clearly delineated from notes that are about the concept in isolation). Second, sometimes a concept's purpose is rather application specific and careful referencing of some application details may be helpful in explaining it. Third, the operational principle is often easier to understand and more compelling if it presumes the existence of actions from other concepts (although preferably it makes clear that these actions are external). Example: the operational principle of a notifying concept may say that 'after a user registers interest in a type of event, if an event of that type occurs, which is conveyed to the concept by the update action, then a notify action occurs, which causes the user to be receive a communication about the event (through a sync with a communication concept)'. **Flaws in overall structure**. These flaws are internal to the concept (not involving other concepts) but are not specific to any particular concept part. - **Mixing of parts**. Concept definitions are broken into parts, each with their own role. If a definition mixes these, it can become hard to understand and information is hard to find. The purpose of the concept should not include information that belongs to the operational principle, by describing a scenario. Example: the purpose of a user authentication concept talks about users registering and then authenticating. The operational principle should not encroach on the purpose by explaining why something happens. Example: the operational principle of a karma tracking concept mentions that when a user receives karma points this is to reward them for good behavior. The operational principle should not encroach on the state declarations or action definitions. Example: the operational principle of a user authentication concept should not say that after a user registers, their username and password are stored, since the definition of the registering action would cover that; the operational principle of a notifying concept can say that a user can add multiple communication channels and then select one as a default, but should not duplicate the state declaration saying that users have zero or more communication channels. - **A class, not a concept**. Some novices are confused about the difference between concepts, which define aspects of functionality, and entities in a data model, which define a set of individuals and their relationships to others (across all aspects of functionality). Symptoms of this confusing include: the concept lacking any interesting behavior (with an operational principle that may have just CRUD actions); conflation of functionality (see above); a state declaration that describes a single object; a concept name that corresponds to an entity or object class rather than some functionality; a purpose that has little substance and may just talk about representing or managing some objects. Example: a concept called User whose purpose is 'to manage users'; whose operational principle involves creating users, setting their properties and looking them up'; whose state is a collection of attributes that conflate aspects of functionality (username, displayname, karma, etc). **Flaws in the concept name**. - **Conveys an object or object type, not functionality**. A concept name that does not indicate what kind of functionality is supported is not helpful. Example: in an online bookstore, calling a concept `Book` would not tell you anything about what it does; in contrast, names like `BookInventory` or `BookCatalog` suggest more (respectively tracking how many instances of a given book are on hand, and helping users find books based on their details). One strategy is to try and use gerunds as names. Regular nouns can be reasonable names too, especially when they describe activities, but they should preferably not coincide with the names of individual types. Example: in a restaurant reservation system, `Reservation` is a reasonable name but would conflict with the same word used to define the type individuals that are created when users make reservations. (Note: in my book, Essence of Software, I did this frequently, assuming that the names of concepts and types could be disambiguated by context. In retrospect this was a mistake as it causes confusion, especially to novices who haven't fully grasped that concepts aren't objects.) **Flaws in the purpose**. - **Vague or superficial purposes**. Defining the purpose of a concept can be very challenging, but is very rewarding. And not having a compelling purpose can be a symptom of not really understanding why the concept is needed at all. Example: the purpose of the `Trash` concept (as introduced by Apple in the early 1980s) is *not*, as many explanations suggest, to ease deletion of items, but the very opposite: to prevent complete deletion and to support *undeletion*. - **Not distinguishing concept from others**. The purpose needs to justify the concept at hand, and so it should distinguish this concept from others. Therefore a purpose that could equal well apply to other concepts, and particularly that does not justify the behavioral design of the concept at hand, is insufficient. Example: a styling concept that lets users associate formatting styles with elements and then update the styles so that all elements are reformatted consistently is fundamentally different from a concept that simply lets users apply a predefined style to an element but does not propagate changes to the style to the elements. So the purpose needs to capture this, by saying something like 'supports consistent styling and restyling of sets of elements' rather than just 'eases the application of styles to elements'. Example: a concept for access tokens should not have a purpose that just mentions authenticating access, since the key point of access tokens is to allow a user to delegate access to multiple parties. - **Overloading**. A concept should have one purpose, and not more than one (since they are likely to be in conflict with one another). Example: the purpose of a reaction concept in a social media app should not be both to convey an emotional response to the author of a post *and* to rank posts by popularity *and* to collect user preferences for recommendations. These are all valid purposes, but belong to separate concepts (which might be synchronized so that a single request from a user causes actions in multiple concepts to occur related to these different functions). **Flaws in the operational principle**. - **Omits setup actions**. The operational principle should be a complete scenario, including whatever actions are needed to set up the state. Example: the operational principle of concept for role based access control should not start with assigning roles to users, but should include the initial creation of the roles themselves. - **Inadequate scope**. There must be enough actions and individuals to demonstrate fulfillment of the purpose. Example: for an upvoting concept, the principle must describe a scenario involving multiple users and items. Saying something like 'after a user upvotes an item, the upvote count has increased by one' does not explain how the concept helps in the purpose (of ranking items by popularity). Example: for a styling concept, to demonstrate how the concept fulfills the purpose of maintaining consistency, the principle has to describe an updating to a style that has been previously applied to multiple elements, not just one. - **Includes unhappy paths**. The operational principle defines the archetypal scenario and does not need to include unhappy paths. Example: a reserving concept need not include canceling of reservations. Sometimes, however, an action that at first sight might be regarded as falling this category turns out to be essential to the purpose of the concept, and should be included. Example: in an access token concept, the principle should mention revoking tokens, since the ability to revoke tokens individually is a key part of the concept design and serves the purpose of delegating to potentially untrustworthy parties. - **Includes error cases**. The operational principle defines the archetypal scenario and does not need to describe how errors are handled. Example: the principle of a user authentication concept need not describe what happens if a user registers with a username that is already taken. **Flaws in the state**. - **Singleton scope**. The state of a concept is almost never about a single individual. A common misunderstanding is to view a concept as if it were an object-oriented class, and to define the state as a list of fields of a single object, when instead the state should have introduced a set of individuals with a relation for each field. Example: a user authentication concept defines the state as having components such as a username and password. The action to authenticate a user will require *looking up* the username to find a matching user, and so the state must be defined as a *set* of users, each with a username and password. - **Reference vs. object**. Those with a programming background are sometimes confused about the way in which individuals are represented in concepts. An individual is always just an identity, so the 'value' or 'content' of an individual will be an identifier. The relations of the state will map this identifier to the identifers representing other individuals (or to primitive values). So unlike in some programming languages, there is no distinction between references to individuals and the individuals themselves. - **Making identifiers explicit**. Because every individual has an identity, there is no need to declare an identifier explicitly for any individual. In some concepts, however, the creation and use of identifers is an important part of the functionality and should be defined explicitly. Example: in a social security system, there will be a concept that manages the creation and uniqueness of social security numbers. Example: a concept for URL shortening will generate identifiers that form the shortened URLs. - **Not sufficient to support actions**. The state must be sufficiently rich to support the actions that are defined. So an action should never refer implicitly or explicitly to state components that have not been declared. Example: in an upvoting concept, if the upvoting action is described as not permitting double voting, then the state must include not only the number of votes that each item has received, but also which user voted for which item. - **Not enough keys for relations**. The state may require that two different individuals are keys for a mapping to some other individual, but is declared so that only one key is mapped. The easiest fix is to introduce a new set of individuals representing a relationship. Example: a concept for managing salaries has a relation that maps employees to their salaries, but this does not account for the fact that an employee can work for several companies at the same time, so the correct mapping is from both employee and company to salary. The fix is to introduce a new individual type (say of jobs), and then an employee has a set of jobs, each with an associated salary and company. - **Vague declarations**. Some informality in state declarations may be acceptable in the early stages of development of a concept, but is likely to be problematic if left unclarified. Example: the state says that a file is associated with 'a version history', without explaining what this means (a sequence of version numbers? of versions of the file? maybe a tree rather than a sequence?). - **Redundant state components**. The state should not include redundancies of the sort that would be typical in an implementation because they might be thought to allow more efficient queries. The relations that appear in state declarations are *semantically* directional, but this does not imply that they can only be navigated in one direction. Example: in a concept for reserving tables in restaurants, the state may define `a set of Reservations with a User` that introduces an implicitly named relation `user` from reservations to users; it would be a mistake to also declare a relation in the other direction, for example by writing `a set of Users with a set of Reservations`, introducing the transpose relation `reservations` from a user to their reservations. The reason is that an action or query can refer to the reservations of a user without this reverse relation needing to be defined. Aside from being redundant, the additional relation would also impose an invariant that would have to be maintained (either by updating one relation whenever the other is updated, or by declaring an invariant and stating that it is implicitly maintained). Note that it is *not* redundant to declare a relation that maps to some set of individuals, and then to declare that set of individuals separately: the relation declaration serves to define the range of the relation, and the set declaration that follows serves to define a general set of individuals and to associate relations with them. Example: the state might declare `a set of Users with a set of Accounts`, which says that each user can have multiple accounts, and then declare `a set of Accounts with a username String`, which introduces the set of all accounts (independently of the accounts of a single user) and associates a username with each account. **Flaws in the actions**. - **Missing actions**. Actions are missing that should have been included. These might be actions for setting up the state or for creating individuals that other actions require as inputs; actions for undoing or compensating for other actions; actions associated with terminating or ending a service. Examples: a concept for templating omits actions for creating templates; a concept for reserving conference rooms omits an action to cancel a reservation; a concept includes creating an account but no action for deleting it. - **Redundant actions**. The collection of actions should be as small as possible while being sufficiently expressive to cover user behaviors. Just because a user might make a request in different forms does not mean that a separate action is required in each case, because synchronizations can be used to link a single user action to multiple concept actions. Example: an authentication concept has an action to allow a user to change their password, which takes the user individual as an argument. One might be tempted to include an additional action that has the username as an argument instead, but such an action would not be needed since the concept already allows the user to be obtained from the username (via the state, and probably also by a defined query). - **Disjunctive specifications**. An action should not have a variety of unrelated effects depending on the state and the action's arguments. A sure sign of this flaw is a 'disjunctive' specification saying 'this happens or this happens or this happens', or that lists multiple cases or has complex if-statements in it. Another symptom is optional arguments, or worse, flag arguments, which, especially in combination, cause a blowup in the number of cases to consider. Instead, a separate action should be defined for each separate case, overloading the name if need be to signal that the actions are different variants of a common action, and optional arguments and flags should be avoided. Example: a shopping cart concept has an action to add an item to the cart that takes an optional count as an argument; instead there should be two overloaded versions of the action, one without a count (that adds a single item), and one with a count (that adds multiples of the item). Example: in a styling concept, an action to define a new style includes an existing style as an optional argument, allowing a style to be defined either as a top-level style or as a child of an existing style; instead, two distinct actions should be defined for the two cases. Example: an action to make a reservation allows an optional argument of an existing reservation, with the intended behavior being that if a reservation is included, the new reservation should replace the previous one. This is an incoherent and needlessly complicated action; having separate actions to cancel and reserve is enough. - **Missing precondition**. There should be no implicit preconditions that must hold to allow the action occur; any preconditions must be explicitly stated. Example: in a reserving concept whose state has an invariant that each reservation has a unique slot (so no slot is reserved twice), an action to create a reservation for a slot cannot implicitly assume that the slot is not already reserved but should specify this as a precondition. - **Precondition appears as a case in the postcondition**. An occurrence of an action should mean something, so it is usually undesirable to specify an action that has no effect despite executing successfully. This can happen when an if-condition is used in the postcondition rather than a precondition, which would prevent the action from happening. Example: in a reserving concept whose state has an invariant that each reservation has a unique slot (so no slot is reserved twice), an action to create a reservation for a slot should not have a postcondition that says that if the slot is already reserved, nothing happens, since the successful execution of the action might suggest that a reservation has been made; instead the constraint should be in a precondition. - **Error handling not needed**. In a design-level concept definition, an action need not specify error outcomes. Example: in a user authentication concept, the action for registering a new user can just have a precondition saying that the username is unique and that the username and password are well formed. In a concept specification intended for generating code, it would be appropriate to include an overloaded version of the action that returns an informative error instead, but this complication can be omitted in the interests of succinctness and focus in a design-level spec. - **Queries presented as actions**. Actions that never update the state and that are executed to obtain information from the state are better viewed as *queries*, which are marked with an underscore at the beginning of their name, and which have a `returns` clause rather than a pre/postcondition. Example: in a reserving concept, one might want to obtain the reservation for a particular slot. Since the state is regarded as visible, it is not necessary to define this specially, but if one wanted to emphasize this particular state lookup, one could define a query for it. Note that in some cases an action may not update the state but its occurrence may still be considered to be important in the behavior of the concept, and in that case, an action would be suitable. Example: in a user authentication concept, the step in which a user is authenticated by successfully matching username and password would be appropriately represented as an action, despite having no effect on the state. This is because being authenticated is a significant event. Contrast this with, for example, a query for extracting the author of a social media post, which is likely to be performed repeatedly in an implementation (for example, whenever the post is displayed), but which does not correspond to an event of significance to any stakeholder. **Inadequate notes**. The notes of a concept provide background justifications, discussions of design variants and other options considered, concerns about the design as given, and so on. The concept definition provides a useful *place* to centralize ongoing work related to the design of the concept. A desire to keep the concept definition clean and simple is admirable, but should not lead to reluctance to use the notes section expansively. Concept designers should bear in mind especially the important role that concepts have in bridging between different teams and different professional specialties. Readers from other teams and specialties may lack the background and assumptions that motivated the concept, so it is a good idea to include notes for these. Each note can be given a short title so that it can be skipped by readers with more background. # Understanding types in concept specifications In a concept definition, some types of individuals correspond to individuals that are always created by the concept. These are *internal* types. Some types of individuals are created by other concepts, and passed in. These are *external* types. **Examples: Internal and external types**. Here are some examples of internal and external types. - In a `Reserving` concept, the type `Reservation`, corresponding to the individual reservations that the concept creates, will be internal. The type `Slot`, corresponding to the slot being reserved, and the type `User`, corresponding to the user who makes the reservation, may be external. - In a `Sessioning` concept that creates long-lived sessions for authenticated users, the `Session` type will be internal, but the `User` type will be external, if users are created by (for example) the `UserAuthentication` concept. - In an `Upvoting` concept, in which a user upvoting an item creates a stored vote, the `Vote` type would be internal, and the `User` and `Item` types would be external. There are also types that do not correspond to individuals: *value* types such as standard primitive types and *composite value* types made from them. These types are assumed to be globally scoped and are therefore not classified as internal or external. **Examples: Value types and composite value types**. The types `Number`, `String`, and `Date` are value types. The type `PostalAddress`, defined as a record comprising a street string, city string and zipcode string, is a composite value type. Concepts should be as *generic* as possible, meaning that the individuals that participate in their actions and that are stored in relations in their state should be allowed to be of any type. This ensures that concepts are not specialized for a particular application or system when they could be applied more broadly. **Examples: Genericity in concepts**. Some examples of genericity: - In an `Upvoting` concept, the items that are upvoted can be any type of item. In the context of use, the items may be social media posts, or comments on a newspaper article, etc. - In a `Commenting` concept, the targets of the comments can have any type, even if in the context of use the targets will be posts (or perhaps other comments). - In an `Authorization` concept, the principals that are authorized to perform certain actions can have any type, even if in the context of use they will be registered users. The types of generic individuals aren't really types at all, but are placeholders or type variables. When a concept is composed with other concepts to form a system, and individuals are passed between concepts (by synchronizing actions), each individual will have its particular type at runtime even if it appears from the perspective of the concept to be generic. **Example: Instantiating a generic type**. Here is an example of how a generic type ends up being instantiated at runtime: - If an `Upvoting` concept is composed with a `Posting` concept in a social media app, so that upvoting is always of posts generated by the `Posting` concept, the items that are stored in the `Upvoting` concept will have the type `Post` even as they are treated as having a generic type with a name like Item by `Upvoting` itself. In conventional approaches, a module can have an external type that refers to a concrete type of some other module. Concept design does not allow this. *Every external type is generic*, ensuring that there are no type dependencies on other concepts, and all concepts are as generic as possible. **Example: Generic external types**. Consider a restaurant reservation system that has a concept called `Availability` that manages slots and their availability, and a concept called`Reserving` that manages reservations of slots. `Availability` associates each slot with a table and a particular time period, so that `Slot_13`, for example, is for `Table_3` between 7:00pm and 8:30pm on January 10, 2026. The `Reserving` concept doesn't need to know this; it simply assigns such slots to users, and ensures that no slot is reserved by more than one user. For the `Reserving` concept, the `Slot` type is external and generic, and it cannot make any assumptions about that type (for example that a slot has a table). This means that `Reserving` cannot have a `reserve` action that takes a table and reserves a slot for it. Instead, the slot has to be found by querying `Availability`, and then reserved using `Reserving`. This ensures that the two concepts remain fully independent of one another, and there is no mixing of functionality. Creating slots for tables and periods belongs to `Availability`; reserving of slots belongs to `Reserving`. In a conventional design, in contrast, it would be possible for `Slot` to be external to `Reserving` but *not* generic. Or in the lingo of type theory, the polymorphism of `Reserving` with respect to `Slot` would not be unbounded, but would be constrained by some interface type. This not only requires a more complicated framework, but worse undermines the clarity and separation of concerns. Within a concept, the name of an external type is local to that concept. It is just a parameter or placeholder, and any name can be chosen that is appropriate to the concept. **Example: Choosing a local name for an external type**. For the restaurant reservation system, for example, we might have an `Availability` concept that manages the availability of dining slots, and a `Reserving` concept that assigns slots to users. Because the slots are generic and external to `Reserving`, they could be referred to within `Reserving` as members of a type named `Resource`, even if at runtime each resource will actually be a slot. # How to write concept specifications Writing concepts down is useful. Most straightforwardly, you want to define what a concept *is*, namely the behaviors that it offers. By focusing on behavior rather than appearance (in a user interface) or implementation (in the code), you can ensure that the same definition can serve different people coming from different perspectives, whether concerned with user experience, software architecture, engineering, marketing, sales, and so on. The concept becomes a bridge between roles. And because a concept can capture common functionality between apps and services, it becomes a bridge also between teams. In practice, being clear about what a concept *is* often means explaining what it *is not*, what it *was* previously, and what it *might be* in the future. The concept definition thus serves as the repository of accumulated knowledge and understanding, of plans for the future, and of ongoing design debates. Obviously defining all these counterfactuals in full detail would be impractical, so they are generally described in informal notes. In addition to saying *what* a concept is, the concept definition should say *why*: why the concept was adopted or invented in the first place, and why particular decisions were made about its behavior. These 'why' questions often feel uncomfortable, but attempting to answer them can be extremely productive (especially when it turns out that different stakeholders have different, and even incompatible, motivations). A concept definition has at least the following parts: - The concept name - The names of external types - The concept's purpose - The operational principle - The state definition - The action definitions Designers tend to augment this minimal definition with additional elements, such as links to related concepts, typical synchronizations associated with the concept, a history of revisions, and (perhaps most importantly) informal notes that explain the definition and its alternatives. The concept *name* should signal the essential functionality of the concept. The functionality will generally be an activity occurring over a prolonged period, so names that would be appropriate for actions that occur instantaneously will not be appropriate. And although the functionality may be primarily associated with one type of individual, it's preferable not to choose a name that could be confused with such a type. **Example: Choosing a concept name for authenticating users**. The concept that handles the functionality of authenticating users might be called `UserAuthentication` or `AuthenticatingUsers`. The name `Authenticate` is not good because it is more appropriate to the action that authenticates a user after they have registered. The name `User` is not good because it could be confused with the type of users, and anyway does not convey the functionality. In concept notation, the external types can be listed in square brackets after the concept name. In a catalog it may be more convenient to have an explicit entry for them. **Example: Recording external types**. To say that the external types of the `Reserving` concept are `User` and `Resource`, you could write ``` concept Reserving externals User, Resource ``` or just ``` concept Reserving [User, Resource] ``` The concept *purpose* explains the motivation for the concept: the value that its functionality brings. The purpose should meet three criteria: - **Need-focused**. The purpose should be stated in terms of needs, and should not just recapitulate or summarize the functionality. - **Specific**. The purpose should be specific to the design of the concept at hand, and not just express some generally desirable quality of the system as a whole, or a need that is universal. - **Evaluable**. A purpose should be a yardstick against which to measure a concept design. It should support an objective evaluation of whether the functionality meets the needs. **Examples: Purpose criteria for Upvoting**. Here are examples of applying these criteria to the purpose of an `Upvoting` concept. An appropriate purpose might be 'to use crowd-sourced approval to rank items.' The criteria would rule out some other purposes as follows: - **Need-focused**. The purpose should not be 'to let users express approval of items' since this just describes what a user does when upvoting an item, and doesn't explain why this has any value. - **Specific**. The purpose should not be 'to increase user engagement.' Even though user engagement is an important driver for many companies designing social media apps, this is a (partial) goal of almost every concept. - **Evaluable**. The purpose should not be 'to improve the quality of user-authored items', since this purpose is too distant from the actual functionality of the concept for it to be a reasonable yardstick for evaluation. **Examples: Purposes for other concepts**. Here are some examples of purposes for various concepts. For simplicity, the external types are omitted. For the `Trash` concept, as invented by Apple in the 1980s, in which deletion places items in a special area from which they can be restored: ``` concept Trash purpose to mitigate accidental deletions by allowing deletions to be undone ``` For the `Styling` concept, as invented for the Alto at Xerox PARC, and popularized in Microsoft Word, in which paragraphs (for example) can be assigned formatting styles that can be updated: ``` concept Styling purpose to enable consistent formatting of documents ``` For the `Notifying` concept, as used in almost all apps, in which users receive notifications when events of interest or relevance happen: ``` concept Notifying purpose provide users with timely notifications of events of interest ``` For the `Role` concept, as used in role-based access control, in which users are assigned roles that have predefined permissions: ``` concept Role purpose simplify assignment of user permissions ``` A concept should have *exactly one purpose*. Obviously, a concept should have at least one purpose. If it has no purpose, there’s no reason for it to exist. More controversially, a concept should have _at most_ one purpose. This might seem strange, especially since some people think that a good design decision solves multiple problems at once (or feeds many birds with one scone, as the politically correct version of the old adage goes). In fact, this is a misconception, and rigorously separating purposes so that each concept has only one purpose is the path to more flexible software, and to avoiding conflating different aspects of behavior in a single concept. A concept with more than one purpose is said to be *overloaded*. **Examples: Overloaded purposes**. - Facebook's `Friending` concept serves two purposes. One is to provide access control so you can limit who sees your posts. The other is to filter your own incoming content, so you can choose whose posts you want to see. These two purposes are not always in alignment; you might want to see someone’s posts but not share yours with them, or vice versa. A better design is to separate these purposes out, with `Friending` determining who can see your posts, and `Following` determining which posts you see. - Many apps have a concept for managing personal user information, such as your username, password, display name and so on. Such a concept conflates authentication functionality and profile display functionality. Splitting into two separate concepts, say `UserAuthentication` and `UserDisplaying`, allows these to be separated and makes it clearer to the user how different data is used (for example, whether the username is displayed publicly). Sometimes a concept brings value to different stakeholders, but there is actually no overloading because you can identify an overarching purpose that both stakeholders are benefiting from. **Example: Overarching purpose**. In a restaurant reservation system, the `Reserving` concept helps diners by giving them confidence that there will be a table for them when they turn up, and it helps restaurant owners know which tables will be available for walk-ins. Both of these benefits can be seen to be aspects of a single purpose: ``` concept Reserving purpose making utilization of resources more predictable ``` A concept's purpose should be only as specific as the concept itself, and it should not refer to the context of use of the concept in the larger application or system. Doing so only makes the concept less reusable. **Example: Concept purpose tied to system context**. If the `Reserving` concept of a restaurant reservation system is designed to allow reserving any kind of resource, it would be inappropriate to write ``` concept Reserving [User, Resource] purpose making utilization of dining slots more predictable ``` since the concept is about generic resources, not dining slots --- even though it will be the case that at runtime each resource within `Reserving` will be a slot. The *operational principle* (or *principle*) is an archetypal scenario that demonstrates how the concept fulfills its purpose. A compelling way to explain how something works is to tell a story. Not any story, but a kind of defining story. It is important to understand that although an operational principle, being a scenario, looks a bit like a use case or user story, it is actually quite different. Its role is to tell a story that explains the essential behavior of the concept and motivates its design. It is *not* intended as a specification of the behavior, which will be provided in full by the states and actions. **Example: A defining story for a service**. The Minuteman Library Network, for example, offers a wonderful service. If I request a book, then when it becomes available at my local library, I get an email notifying me that it’s ready to be picked up. Note the form this scenario takes: _if_ you perform some actions, _then_ some result occurs that fulfills a useful purpose. Many kinds of mechanism can be described in this way. **Examples: More mechanisms defined as stories**. - If you make social security payments every month while you work, then you will receive a basic income from the government after you retire. - If you insert a slice of bread into the toaster and press down the lever, then a few minutes later the lever will pop up and your bread will be toast. - If you become someone’s friend on social media and they then publish an item, you will be able to view it. A concept's operational principle is a story that explains the functionality of the concept, showing how it fulfills the concept's purpose. The key elements of the story are the actions and states of the concept. **Examples: A simple operational principle**. The principle for the `Reserving` concept is very simple and minimal, because the concept is very general so there are none of the details that you'd expect if the concept were specialized to, for example, restaurant reservations, airline seat assignments or conference room bookings. - **Reserving**. If a user reserves a slot, they can then redeem it at a later point. The operational principle is the scenario that motivates the existence of the concept. It does not have to include variant or failure cases. For some concepts though, an action that represents canceling or undoing is actually central to the concept's purpose, and then it must appear in the principle. **Examples: Operational principles of two user authentication concepts**. Here are principles of two different concepts, that show a crucial difference between them. For standard user authentication, deleting or suspending the account is not fundamental. But for token based authentication, the revocation of tokens is central, since the very reason for issuing multiple tokens to different parties is to allow one to be revoked without affecting another. - **UserAuthentication**. If you register with a user name and password, and then you login with that same user name and password, you will be authenticated as the user who registered. - **TokenBasedAuthentication**. A user can create access tokens for a resource and pass them to other parties, who may then provide their token and obtain access. If the user revokes a token, the party who received that token will no longer be able to use it to obtain access. A principle should generally be as simple as possible, using as few individuals as possible. But sometimes multiple individuals and occurrences of the same action are needed to convey the essence of the concept. **Example: Operational principle that requires multiple individuals**. The essence of upvoting is that an item (eg a social media post) is ranked according to the aggregate upvotes that it receives from multiple users. Here is an bad principle because it doesn't convey this idea: - **Bad principle for Upvoting**. If a user upvotes an item, the number of votes on the item increases by one. and here is a better one: - **Good principle for Upvoting**. If a multiple users upvote a collection of items, the items can be ranked according to the number of votes that each received. **Example: Operational for styling**. Here is another example of a principle that requires more than one occurrence of an action: - **Principle for Styling concept**. After a style is defined and applied to multiple paragraphs, updating the style will cause the format of all those paragraphs to be updated in concert. A good principle should satisfy three criteria: - **Goal focused**. The principle should demonstrate how the purpose is fulfilled. Often the last action or state in the principle corresponds to some goal that has been achieved. This is a key respect in which principles often differ from user stories, which may describe a scenario that has an observable outcome but not the value that motivates the design. - **Differentiating**. The principle should distinguish the functionality of the concept from other concepts, especially simpler ones, for example by including actions that are special to the concept and essential to its behavior. - **Archetypal**. The principle should not include corner cases that are not essential to demonstrating how the concept fulfills its purpose. **Examples: Applying the criteria**. - **Goal focused**. The principle for `Reserving` should not just say that having executed a reserve action for a resource, that resource is now reserved for that user. The value comes when the user actually redeems the reservation (for example, by being seated at the restaurant), so redeeming must be included in the principle. - **Differentiating**. The principle for `TokenBasedAuthentication` should include tokens being given to multiple parties and being revoked. Without these complications, the concept would not be needed. If there were just one user, for example, a simple password-based user authentication concept would suffice. - **Archetypal**. The principle for `Reserving` should include reserving a resource and redeeming the reservation, but it does not need to include the possibility that the reservation is canceled, even though the concept will generally include an action to support this. The reason such cases are not needed is that the states and actions sections fully define the behavior of the concept and thus define all possible scenarios implicitly; the role of the principle is to identify the essential scenario that motivates the design and shows how the purpose is fulfilled. The operational principle can be written formally and precisely, but because it plays a crucial role in telling the story of the concept it can be preferable to write it in a way that makes it as accessible as possible. **Example: formal and informal principles**. As an example, consider the `UserAuthentication` concept, whose principle might be expressed formally or informally. The formal version is more succinct but a bit cryptic, since it relies on understanding how the variables are bound. - **Formally**. After register(username, password):(user), login(username, password):(user). - **Informally**. If you register with a user name and password, and then you login with that same user name and password, you will be authenticated as the user who registered. A concept definition should *not* make reference to the actions or states of other concepts within the sections describing its own states and actions. But in the operational principle (and to a degree within the purpose), it can make sense to imply the existence of actions and states belonging to other concepts. **Example: Operational principle that suggests actions from other concepts**. For example, the operational principle of the `Notifying` concept might be: 'If a user registers for a particular event type, then when an event of that type occurs, the user will be notified.' This `Notifying` concept might have an action called `notify` that is executed in response to an event, but that action is unlikely to actually notify a user. Instead, it would typically be synchronized with an action from another concept that causes some communication to occur (for example, an email or text or in-app announcement). So strictly speaking, the operational principle of the `Notifying` concept assumes this synchronization. Likewise, when we specified the purpose of `Notifying` above as 'provide users with timely notifications of events of interest', it should be recognized that this purpose is contingent on the presence of other actions that will actually convey the information to the user. The *state* section of the concept defines the state that the concept stores in order to perform the actions. In behavioral terms, the state of a concept is a collection of relations, each holding some particular relationships. The state may be used to determine whether an action is permitted to happen, and to determine which outputs the action produces. When an action is executed, the state is updated. In this sense, the state represents what the concept remembers about the sequence (or trace) of action occurrences. **Example: State of Upvoting concept**. The state of an upvoting concept will obviously need to track how many times each item has been upvoted, and each occurrence of the `upvote` action will increment the count for the item given. But if double voting is to be prevented, the `upvote` action will have to be permitted only when that user has not already upvoted that item. And this implies that the state will have to hold not only how many votes an item has, but also which users those votes came from. **Example: State of Reserving concept**. A `Reserving` concept used in a restaurant reservation system may have a `cancel` action that allows a reservation to be cancelled. In one possible design, cancellation causes the reservation to be deleted and forgotten about. In that case, the action would not need any particular support in the state. But in another design, no reservations are ever forgotten about; this would allow the user to see, for example, a list of active reservations along with those that have been cancelled. To support this, the state would need to record additional information about which reservations are active and which have been cancelled. The state section of a concept is a data model. Unlike a conventional data model which represents the full state of an entire system, a concept data model represents only the part of the state that is relevant to the concept's behavior. **Example: Partial data models for user-related concepts**. In a conventional data model, a user might have attributes such as username, password, display name, and so on. In a concept state, only those attributes that are relevant to the concept would be included. In a `UserAuthentication` concept, for example, the mapping from user to password would be included (and perhaps also the username, although even that might be separated into a naming concept). The display name is not relevant to authentication, so would not be included, but would appear in a concept such as `UserDisplaying` that manages how user information is displayed for other users, and the email address likewise would appear in a A note about the word *state*. There is a potential ambiguity, because the word is used with two subtly different meanings. One meaning is a particular state out of all possible states. The other meaning is the definition of the set of all possible states. It is in this second sense that the word is used as the title for the state section in a concept definition. Example: Two usages of the word state. In the first meaning, you might say that the 'state of the light bulb is `off`'. In the second meaning, you might say that the 'state of the light bulb' is defined by ``` lightBulbState: {ON, OFF} ``` The state of a concept can be written in different ways. Simple State Form (SSF) was developed as a notation for states that is intended to be rich enough to describe complex data models, natural-sounding enough to be comprehensible to non-experts, and easy to translate into code. A separate document describes SSF, so here we will focus on explaining some of the key features. A declaration introduces a set of individuals and then any number of relations involving that individual. **Example: State declaration**. The following declaration introduces a set of reservation individuals, along with two relations, one associating reservations with users, and one associating reservations with slots: ``` a set of Reservations with a User a Slot ``` Note that there are actually three components of the state being declared here: the set and the two relations. The set defines the reservations that exist in the scope of the concept, so the assertion that some reservation exists is tantamount to saying that it's a member of this set. Note that declaring a set of individuals does *not* mean that the concept 'owns' those individuals. The individuals that are owned by a concept are those that it creates, which are those that have an internal type. Individuals of an external type can still be stored within the concept. In this case, the concept stores external individuals with whatever relations it attaches to them. **Example: Storing external individuals**. A concept for upvoting which declares the type User to be external may include a state component that is a set of users, as follows: ``` concept Upvoting [User, Item] ... state a set of Users with a set of Votes a set of Votes with an Item ``` This means that the concept stores a set of users who have voted, and for each user stores the set of votes they issued. All votes are stored as a separate set and each vote is associated with an item. Both users and items are external; only the votes are internal and are created by the concept. But note importantly that the concept can still store a set of users with their votes. Do not be mislead by object-oriented thinking which might suggest that there can only be one declaration of `a set of Users` amongst multiple concepts. On the contrary, this is the primary means by which concepts attach their own properties to individuals. Each relation has a name that can be specified explicitly, or if not specified, is given implicitly by the name of the type that follows. **Example: Implicit relation names**. In this declaration ``` a set of Reservations with a User a Slot ``` the two relations have the implicit names user and slot, so the declaration is equivalent to this one in which the relations are explicitly named: ``` a set of Reservations with a user User a slot Slot ``` A declaration constrain the multiplicity of each relation, according to whether one of the keywords `unique`, `optional` or `set` is used. **Example: Multiplicities of relations**. This declaration introduces a relation called `friends` that associates each user with a set of zero or more users that are their friends: ``` a set of Users with a friends set of Users ``` This declaration introduces a relation called `displayName` that associates each user with one string or no string: ``` a set of Users with an optional displayName String ``` This declaration introduces a relation called `userName` that associates each user with a string that is unique -- that is, no two users share the same string: ``` a set of Users with a unique userName String ``` This declaration says that each reservation has a user and slot associated with it, and the slot is unique -- that is, no two reservations share the same slot: ``` a set of Reservations with a User a unique Slot ``` A note about naming relations. The name of a relation should generally convey its meaning. Often however there is only one plausible meaning given the type, and in that case it's common to use the type itself as the name. This aligns with a view of the relation as a function that maps one individual to another. **Example: Names for relations**. The declaration ``` a set of Reservations with a user User a slot Slot ``` introduces two relations, `user` and `slot`, that viewed as functions map a reservation to the user and slot associated with it. These names only convey the meaning partially though. What exactly does the relationship between the reservation and the user mean? The assumption here is that there is a single user associated with a reservation who both made the reservation (who the reservation is *by*) and who plans to turn up to redeem it (who the reservation is *for*). If this mattered, especially if we wanted to store both relations, we would use more meaningful names: ``` a set of Reservations with a by User a for User ... ``` The actions section of the concept definition lists each type of action with the types of the individuals that participate in it, and then describes the relationships that must hold for the action to occur, and how the action adds and removes relationships. The arguments of an action can be divided into the *inputs*, which are selected by the instigator of the action, and the *outputs* that are results of the action. An action can have any number of inputs or outputs. In particular, it may have no output or more than one. It's common in programming languages to name inputs but not outputs since there can generally only be at most one output. But since concept actions allow more than one output, for uniformity all outputs are generally named. Each input or output is given a name, which is a variable or placeholder for the actual input or output in an occurrence of the action, and a type. Arguments are usually individuals but may also be *values* (such as primitive types or composites of primitive types). There is nothing in concept design corresponding to passing a mutable object. The part of the action definition that declares the action name and the names of the inputs and outputs with their types is called the *signature* of the action. **Examples: Action signatures**. Here are some examples of action signatures. The `reserve` and `create` actions each have a single output. The `cancel` and `setUnavailable` actions have no output. ``` reserve (user: User, resource: Resource) : (reservation: Reservation) cancel (reservation: Reservation) create (restaurant: Restaurant, time: Time, date: Date): (slot: Slot) setUnavailable (slot: Slot) ``` Each action is either instigated externally, or is instigated by the concept itself. Actions instigated by the concept itself are called *system actions* and are marked with the keyword `system`. **Example: System actions**. A `Notifying` concept may have an action called update which is executed when an event of interest occurs, and an action called notify that is then executed spontaneously by the concept itself to notify each user that registered interest in that type of event: ``` update (type: EventType, link: EventLink) system notify (user: User, type: EventType, link: EventLink): (notification: Notification) ``` Should a system action's argument be inputs or outputs? Since a system action is both instigated by a concept and responded to by the same concept, the distinction is less important than for regular actions, and tends to be used to distinguish the arguments that characterize the action occurrence (the inputs) and the ones that result from it (the outputs). **Example: Notify system action**. In the notify `action`, the action occurrence is characterized by which user and which event type it is for, so these are given as inputs. The result is that a notification is created, so that individual is an output. In conventional function call settings, only the outputs of a function call can be used to constrain subsequent calls. This is not true in concept design, since a synchronization can bind variables based on inputs as well. **Example: Synchronization for notification**. A sync for notification might specify that when the system `notify` action happens, a `send` action of a Messaging concept follows, with the same arguments (and the event type and link concatenated into a single string): ``` when Notifying.notify (user, type, link) then Messaging.send (user, type ^ link) ``` An action may have different effects depending on what types of arguments are given. To express this, two different actions can be defined with the same name. This is the standard programming languages technique known as *overloading*. This is the right way to deal with optional arguments. **Example: An overloaded action**. A `Messaging` concept might have a `send` action that sends a message to a user, that takes the user and the message as inputs, and optionally also takes a communication channel (such as text or email), using a preferred default when the channel is missing. ``` send (user: User, message: String) send (user: User, message: String, channel: Channel) ``` When describing concepts at the design level, there is no need to consider the possibilities of errors. If an action is presented with inappropriate arguments, it simply does not occur. But when you're ready to generate code from a concept definition, you may want to define an overloading of an action that allows it to be invoked and produce an error results. **Example: Overloading for distinguishing an error case**. At the design level, the register action of a UserAuthentication concept may have a single signature ``` register (username: String, password: String): (user: User) ``` and a specification that says that the action can only happen if the username and password are well formed and the username is not already associated with an existing user, and that if so, a new user is output. At the coding level, you could define two overloads ``` register (username: String, password: String): (user: User) register (username: String, password: String): (error: String) ``` so that the register action happens irrespective of the inputs, but in the case in which an input is ill-formed, returns an error string (explaining what's wrong) rather than a user. As mentioned earlier, an action's occurrence is generally contingent on certain relationships holding prior, and it causes relationships to be added and taken away. In state terminology, there is a *precondition* that holds in the state before execution of the action, and a *postcondition* that holds after. The precondition can involve the inputs too, so it's a constraint on the combination of inputs and the state before. If there are outputs, the postcondition will involve them too, so it's a constraint on the combination of the state before, the inputs, the outputs and the state after. The precondition is labeled with the keyword `requires` since it's a condition that is required to hold before the action; the postcondition is labeled with the keyword `ensures` since it's a condition that the concept ensures holds after the action. **Examples: Preconditions and postconditions for actions**. A `Reserving` concept with this state ``` a set of Reservations with a user User a unique slot Slot ``` may have a `reserve` action defined as follows: ``` reserve (user: User, slot: Slot): (reservation: Reservation) requires no reservation already exists for slot ensures creates a new reservation with given user and slot and returns it ``` and a `cancel` action defined as follows: ``` cancel (reservation: Reservation) requires reservation exists ensures removes reservation ``` Note that the existence of reservations is with respect to the set defined by the state declaration (`a set of Reservations`). Creating a new reservation means adding a new individual to this set, and removing a reservation means removing a reservation individual from this set. Conceptually, an action adds and removes relationships (that is, adds and removes tuples of relations), but it's common to write the postcondition more informally as if the relations were fields of objects being updated. It's important to remember, however, that this is just a convenient way to describe how the relations change, and there aren't any composite objects being stored. This is important because when individuals are passed between concepts (in synchronizations), it's only the individuals that are being passed (that is, their identities), and none of their properties are passed with them. **Example: Changes to a relation expressed as field updates**. A `UserAuthentication` concept with this state ``` a set of Users with a unique username String a password String ``` may have a `changePassword` action defined as follows: ``` changePassword (user: User, newPassword: String) requires user exists ensures sets password of user to newPassword ``` Strictly speaking, the effect of the action is to remove any relationship `(user, oldPassword)` that previously existed and to add a new relationship `(user, newPassword)` . It's intuitive to talk about 'setting the password of user' but remember that `user` is just an individual, associated by relations in the state to a username and a password. There is no 'user object' that *contains* a username and password. When a postcondition does not mention a state component, that state component is assumed to be unchanged. That is, there is an implicit *frame condition*. **Example: Implicit frame condition**. In the `changePassword` action ``` changePassword (user: User, password: String) requires user exists ensures sets password of user to be the given password ``` the postcondition only mentions changing the password of the user, so the username is unaffected. Remember that a declaration of the form ``` a set of As with a B a C ``` introduces a set of individuals of type `A`, and two relations (one from individuals of type `A` to individuals of type `B`, and one from individuals of type `A` to individuals of type `C`). This declaration does *not* introduce a set of objects each of which *contains* a `B` field and a `C` field inside it. This when an individual of type `A` is presented as an input to an action, the input is just the identity of the individual. The reason the action can refer to the `B` individual associated with it is that the relation from `A` to `B` is in the state of the action's concept. A different concept might have a relation that maps each `A` to a different type of individual (`D`, say). Thus the individual can have different associations or properties in different concepts. This is key to the separation of concerns that concept design encourages. In specification circles, this strategy is called *view separation* because there are two views of the same individual. **Example: Separation of concerns by view separation**. A `UserAuthentication` concept and a `UserDisplaying` concept may have different views of a user. In `UserAuthentication`, a user may be associated with a username and password: ``` concept UserAuthentication state a set of Users with a unique username String a password String ``` and in `UserDisplaying` a user may be associated with a displayname and bio: ``` concept UserDisplaying state a set of Users with a displayName String a bio String ``` How the state of concept changes (with relationships coming and going) as actions happen is part of its observable behavior. In contrasting to many programming language notions of module state, there is no sense in which the state is 'private' to the concept and not visible from the outside. There is therefore no need to define 'observer' functions to read the state. Nevertheless, it can be helpful to define certain common access patterns as *queries*. Unlike an action, a query can always be executed (has no precondition), never updates the state, and its occurrence is not generally a significant event. The keyword `returns` is used to label the constraint defining the results it produces. To distinguish queries from actions, it's conventional to start their names with an underscore. A query always returns a *set* of results. If the query declares multiple outputs, each result will include a value for each of the outputs. **Example: Query**. The `Reserving` concept may have a query that takes a user and returns the slots that that user has reserved: ``` _getReservedSlots (user: User): (slot: Slot) returns slot for each reservation of the given user ``` If there is no reservation for the given user, the set of results will be empty. If there is one reservation, a single slot will be returned. If the user has more than one reservation, then multiple slots will be returned. # A notation for state declarations in concept specifications ## Purpose Simple State Form (SSF) is a syntax for data modeling that is designed to be both easy to read (especially by non-technical people) and also easily translatable into a formal database schema (either by an LLM or by a conventional parser). It is intended to be compatible with collection databases (such as MongoDB), relational databases (such as SQLLite), relational modeling languages (such as Alloy), and also graph databases (such as Neo and GraphQL). SSF was motivated by the need for a simple language for state declarations for concepts in concept design. ## Semantic Features The key semantic features of SSF are: the ability to declare sets of individuals, along with relations that map them to other individuals or primitive values, and subsets of these sets, with additional relations. A basic set of primitive types is provided, as well as enumerations. The language is first-order, so an individidual can be mapped to a set of individuals or scalars, but not to a set of sets. Union types are currently not supported. ## Grammar - *schema* ::= ( *set-decl* | *subset-decl* )\* - *set-decl* ::= \[ "a" | "an" \] ("element" | "set") \[ "of" \] *individual-type* \[ "with" *field-decl* \+ \] - *subset-decl* ::= \[ "a" | "an" \] *sub-type* ("element" | "set") \[ "of" \] ( *individual-type* | *sub-type* ) \[ "with" *field-decl* \+ \] - *field-decl* ::= \[ "a" | "an" \] \["optional"\] \["unique'\]\[*field-name*\] (*scalar-type* | *set-type*) - *scalar-type* ::= *individual-type* | *parameter-type* | *enumeration-type* | *primitive-type* - *set-type* ::= ("set" | "seq" ) \[ "of" \] *scalar-type* - *enumeration-type* ::= "of" (*enum-constant* "or" )\+ *enum-constant* ## Grammar conventions - \[ x \] means x is optional - In ( x ), the parens used for grouping, and do not appear in the actual language - a | b means either a or b - x \* means an iteration of zero or more of x - x \+ means an iteration of one or more of x ## Grammar constraints - A *field-name* may be omitted only for declaring a field of *individual-type* or *parameter-type*. Omitting the field name is equivalent to including a name that is the same as the name of the type but with the first character in lower case. - The hierarchy that is specified by *subset-decls* cannot contain cycles. Thus, a *subset-decl* may not, for example, declare a subset with a *sub-type* that is the same as the *sub-type* that it is a subset of. - The *field-names* within a *set-decl* or *subset-decl* must be unique. Also, within all the decls that are in the hierarchy beneath a *set-decl*, *field-names* must be unique. - A *field-decl* that has a *set-type* cannot use the *optional* or *unique* keyword. - A *field-decl* cannot have both the *optional* and *unique* keywords. ## Lexical considerations: identifiers - The identifiers *enum-constant*, *field-name*, *sub-type*, *individual-type*, *parameter-type* and *primitive-type* are sequences of alphabetic characters, digits and underscores, starting with an alphabetic character. The alphabetic characters in an *enum-constant* must all be uppercase. A *field-name* must start with a lower case alphabetic character. A *subset-name*, *individual-type*, *parameter-type* or *primitive-type* must start with an upper case alphabetic character. - The standard values from which a *primitive-type* is drawn are "Number", "String", "Flag", "Date", "DateTime". ## Lexical considerations: layout - The language is whitespace-sensitive to ensure unambiguous parsing - Each declaration must occupy a single line - Field declarations must be indented beneath the set declarations they belong to - Types can optionally be pluralized, so "a set of Strings" is equivalent to "a set of String" - Type names must always be capitalized ("User") and field and collection names are not capitalized ("email") - Enumeration values (and no other names or types) are in uppercase - The name of a field can be omitted only for an individual type or a set of individual types, in which case the implicit name of the field is the lowercased version of the type name, singular for a scalar and plural for a set. ## Semantics - Set and subset declarations introduce sets of individuals, named by *individual-types* and *sub-types*. Every member of a subset is expected also to be a member of the corresponding superset. For a regular individual type, adding an individual to a set will typically correspond to creating the individual; in contrast, adding an individual to a subset involves taking an existing individual and making it belong to the subset. This is not true for a parameter type, which represents individuals allocated elsewhere, and which can therefore be added to a top level set without needing to be created. - The subsets of a set can overlap. Subsets offer a way both to classify individuals (in a traditional subtype hierarchy) and also a way to declare relations on existing sets without extending the set declaration. - When the keyword "element" is used rather than "set" in a set or subset declaration, the declared set is constrained to contain exactly one individual. - The value of an individual is just its identity, so an individual should not be thought of as a composite. But it is convenient to group the relations associated with an individual as if they were properties of the individual. It is important to understand that grouping the relations as 'fields' of an individual is just a way to simplify the notation and does not imply that the individual is an 'object' in the object-oriented sense that 'contains' its fields. - Every field can be viewed as a relation that maps an individual to a set of values that may be empty or may contain a single value or multiple values. An optional scalar field corresponds to the empty case. A field with a set type should *not* be declared as optional; instead an empty set should be used when there is no value to map to. - When a field is marked as unique, it defines a relation that maps at most one individual to a given individual. - A field that is declared with the seq keyword is like one declared with the set keyword, except that the elements are ordered. ## Examples A set of users, each with a username and password, both strings: a set of Users with a username String a password String A set of users each with a unique username: a set of Users with a unique username String A set of users, each with a set of followers who are users: a set of Users with a followers set of Users A set of users, each with a profile (using the ability to omit a field name, so that the implicit field name is "profile"): a set of Users with a Profile A set of users with a status that is enumerated: a set of Users with a status of PENDING or REGISTERED A singleton set used for global settings an element GlobalSettings with a deployed Flag an applicationName String an apiKey String A set of users, and a subset that have been banned on a particular date and by a particular user: a set of Users with a username String a password String a Banned set of Users with a bannedOn Date a bannedBy User A subset without any relations: a set of Users with a username String a password String a Banned set of Users A set of items, classified into books and movies: a set of Items with a title String a created Date a Books set of Items with an isbn String a pageCount Number an author Person a Movies set of Items with an imdb String a director String an actors set of Persons a set of Persons with a name String a dob Date A mapping defined separately on a set, using a subset (defining a relation called *followers* mapping users in the subset *Followed* to users): a set of Users with a username String a password String a Followed set of Users with a followers set of Users An implicitly named field (called *profile*, relating *Users* to *Profiles*) a set of Users with a Profile An implicitly named set-typed field (called *options*, relating *Questions* to Options) a set of Questions with a set of Options A model of a simple folder scheme in which folders and files have names: a set of Folders with an optional parent Folder a name String a RootFolder element of Folder a set of Files with a Folder a name String A model of a Unix like scheme in which names are local to directories: a set of FileSystemObjects a Files set of FileSystemObjects a Directories set of FileSystemObjects with a set of Entries a RootDirectory element of Directories a set of Entries with a name String a member FileSystemObject A schema is easily translated into a diagram as follows: - Create a node for each set or subset declaration and label it with the set or subset name. - For each subset declaration, draw a dotted arrow to the node that it is declared to be a subset of. - For each field of a set or a subset, draw a solid arrow labeled by the field name to the target type, which is either a set or subset node, or a fresh node with an appropriate label for a primitive type. - An enumeration is drawn by introducing a set node for the type as a whole, and a subset node for each of the enumeration constants. # How to write concept synchronizations Synchronizations are an essential tool for maintaining independence of concepts. Concepts define independent aspects of functionality, which must be woven together to form the functionality of the system as a whole. In a conventional design, services are often connected by explicit calls from one service to another. This causes coupling between the services, rendering them dependent on one another. The result is that one service cannot be understood in isolation, nor can the implementation of one service be used without another service being present also. Calls may play two different roles: either that one service *uses* another service to fulfill its purpose, or it *coordinates* so when something happens within the service with something in another service. **Example: Use and coordination in conventional services**. A restaurant reservation system may have three services, one for making reservations of dining slots, one for checking the details and availability of slots, and one for notifying users. The reservation service may call the availability service to obtain an available slot to reserve; this is an example of a *use*. The reservation service may call the notification service to notify a user when a reservation has been successfully made; this is an example of a *coordination*. Concept design maintains the full independence of concepts from design through implementation. First, the allocation of functionality to concepts avoids the kind of *fragmentation* that would make the use of other concepts necessary. Second, *synchronizations* provide all coordination between concepts completely externally to the concepts, without impinging on the definitions of the concepts themselves. **Example: A concept version of coordination**. A restaurant reservation system may have three concepts, a `Reserving` concept for reserving slots, an `Availability` concept for creating slots and finding available ones, and a `Notifying` concept for handling user notifications. The `reserve` action of the `Reserving` concept would not take a restaurant and time as arguments, and thus have to somehow delegate to `Availability` (which would not be allowed); instead, it would take only the user and the slot as arguments, and its effect would be to reserve the slot for the user. A synchronization would connect a user's action requesting a slot for a restaurant at a particular time with a query of the `Availability` concept that finds an appropriate available slot, and a call to the action of the `Reserving` concept to reserve that slot. For the notification, a separate synchronization would specify that whenever a successful reservation is made (by the `reserve` action in the `Reserving` concept) the reserving user is notified (by the `notify` action in the `Notifying` concept). Externalizing synchronizations has several important advantages: - **Preserving concept independence**. As explained already, it allows concepts to remain fully independent of one another, free of any references to external concepts. - **Making coordination explicit**. In conventional designs and implementations, calls from one service to another are often buried in the design documents or code of the service, so it is hard to get a full picture of the ways in which actions in one service can lead to actions in another. With syncs, the causal links are all made explicit and can be easily understood, audited, analyzed, etc. The declarative form of syncs also means that there are no complex control structures to analyze. - **Factoring out application-specific behaviors**. Some of the ways in which concepts are coordinated are fairly generic and typical, but many of them are specific to the particular application or system. This means that separating coordination out ensures that concepts can remain application-independent, with the application-specific aspects of behavior factored out into syncs. - **Improved scalability**. Coordination between services in an architecture can introduce performance bottlenecks, especially when calls are synchronous and blocking. By treating syncs separately, it becomes possible to implement them in more flexible ways, for example, allowing them to be executed in different orderings and interleaved in different ways. If there are syncs that action A causes action B and action C causes action D, then the definition of the syncs will generally allow orderings such as ACBD, which preserve the causal links but do not impose the rigid serialization ABCD that a conventional call structure would require. The rule that concepts are independent and may not reference each other, and the use of syncs for external coordination, make concept design distinctive. But these practices are commonly used in contemporary software development, albeit often in more complicated forms. Making concepts independence and requiring that all references to shared individuals rely only on the identities of the individuals is an application of the idea of *unbounded polymorphism* or *parametricity*, widely used in functional programming. Externalizing coordination with action synchronizations is an example of an *event-driven architecture*, with the simplification that there no events distinct from API functions (that is, actions), and the syncs play a role similar to the anti-corruption later pattern in domain driven design. Syncs can be used to coordinate concepts for a variety of different purposes: - **Authentication and authorization**. A sync can prevent an action from occurring unless the actor who performs it is authenticated appropriately and has the requisite authorization. - **Filling in contextual information**. A sync can provide arguments to an action from the context of the execution. - **Notifications and logging**. A sync can cause notifications to users when significant actions occur, and can also ensure that logs are maintained for auditing, analytics, etc. - **Multi-purpose user actions**. A sync can allow a single user action to result in multiple concept actions that have different purposes but may all be intended by the user. - **Combining actions into a single functions**. A sync can enable a function to be performed that requires a combination of actions in different concepts. - **Executing default actions**. A sync can cause actions to occur by default without the user having to request them explicitly. - **Maintaining invariants**. A sync can ensure that application-level invariants that cross concepts are maintained, by causing the occurrence of an action in one concept to result in an action in the other that preserves a constraint between the states of the two concepts. - **Implicit execution of actions**. A sync can execute an action that a user is presumed to have performed implicitly, on account of some other action occurring. - **Hydrating responses**. A sync can be used to hydrate a response to a request with information from different concepts. **Examples: Sync purposes**. Here are examples of syncs that fulfill each of these purposes: - **Authentication and authorization**. In a banking system, a sync may ensure that when a user requests a withdrawal on an account, the withdrawal only happens if the user is authenticated as the owner of the account. In a file system, a sync may ensure that when a user requests creation of a file, the user is authorized to create a file in the particular directory's access control list. - **Filling in contextual information**. In a social media app, when a user creates a new post, the action that creates the post may take the user who created the post as an argument; in a web app, this user individual would not be given explicitly in the request from the client, but would be inserted by a sync that obtains the user identity by querying a sessioning concept to find the user associated with the active session. - **Notifications and logging**. In a social media app, a sync may ensure that when a comment is made on a post, the author of the post is notified. In an electronic health record system, a sync may ensure every access to a patient record causes a write to a log that can be audited to make sure that all accesses were made appropriately by the right personnel. - **Multi-purpose user actions**. In a social media app, when the user likes a post, a sync may execute an action in an upvoting concept (to promote the post's popularity), an action in a recommending concept (to record that the user likes this kind of post and should be shown more similar posts later), an action in a reacting concept (to convey the like back to the author), and, more insidiously, an action in advertising concept (to target ads at the user related to their preferences). - **Combining actions into a single functions**. In a restaurant reservation system, a user's request to reserve a table at a restaurant at a given time may be fulfilled with a sync that calls a query on an availability to concept to find a suitable slot, and then calls an action on a reserving concept to reserve that slot. - **Executing default actions**. An email application may have a concept that determines whether messages are spam, and a separate concept that whitelists senders so that their messages are never regarded to be spam. If a user marks a message as spam, but the sender is whitelisted, a sync could execute by default the action that removes the sender from the whitelist. - **Maintaining invariants**. In a restaurant reservation system, the reserving concept holds in its state which slot has been reserved by which user; and the availability concept holds in its state which slots are still available and what their associated restaurants and times are. When a slot is reserved, both concepts should be updated, with the slot being assigned to the user in the reserving concept, and the slot being marked as no longer available in the availability concept. This preserves the invariant that the set of available slots is exactly the set of slots that are not reserved. Cascading deletes are another (and very common) example of invariant preservation. - **Implicit execution of actions**. A communication app may have a concept that tracks which messages have been read, and that provides an action to mark a particular message as read. The user is not generally expected to call this action, although it can be made available to the user (along with an action to mark a message as unread). Most often the action will be called by a sync that infers it from the occurrence of another event: for example, that the user has displayed the message and some amount of time has passed. - **Hydrating responses**. A web service may provide actions that return an amalgamation of information from different concepts. In an online store, for example, the action that creates an order may return not only the order-id but also the details of the order, including the titles of the ordered items. These pieces of information are likely to come from different concepts: the order-id from an ordering concept but the item titles from an item cataloging concept. A sync can be used to assemble the results of queries across different concepts into a single response. The form of a synchronization. A synchronization has a simple declarative structure, taking the form: *when* certain actions have happened, *where* some conditions hold, *then* some additional actions are invoked. The actions that appear in the *when* clause may contain variables that are bound by the synchronization, which can then be used in the other clauses; likewise the where clause can bind additional variables used in the *then* clause. **Example: A synchronization for reserving a restaurant slot** . The following sync takes a request to reserve a slot at a restaurant by a given user at some given time, finds an available slot using the state of the `Availability` concept, and then reserves the slot using the reserve action of the `Reserving` concept: ``` sync ReserveRestaurantSlot when Requesting.reserve (user, restaurant, time) where slot is an available slot for restaurant and time in Avaiability then Reserving.reserve (user, slot) ``` Note that this example uses a concept called `Requesting` that models the behavior of the user. Thus when the user requests a reservation the action `Requesting.reserve` occurs (and can never be rejected). The sync will determine whether it causes an actual reservation to be made via the `Reserving.reserve` action. The request can occur without the follow-up action if no slot is available at the requested time. Here is a version of the same sync, but using a query which is assumed to have been declared in the Availability concept, and which returns a set of available slots for the given restaurant and time. Note that the query returns a set of slots, and the constraint in the where clause asserts that `slot` is one of those. ``` sync ReserveRestaurantSlot when Requesting.reserve (user, restaurant, time) where Availability._availableSlots (restaurant, time): (slot) then Reserving.reserve (user, slot) ``` **Example: A synchronization for providing the author of a social media post** . The following sync takes a request to create a social media post and calls the appropriate action with the author of the post obtained from the session token: ``` sync CreatePost when Requesting.post (session, content) where Sessioning._getUser (session): (user) then Posting.post (user, content) ``` A sync can require multiple actions to happen before it fires, and it can also cause multiple actions to happen. **Example: A sync that causes multiple actions to happen**. In a social media app, when a user likes a post, three actions might follow (the upvoting of the post so that it is ranked more highly in feeds; the liking of the post for subsequent recommending; and reacting to the post to convey approval to the author): ``` sync LikePost when Requesting.like (user, post) then Upvoting.upvote (user, post) Recommending.likes (user, post) Reacting.reacts (user, post) ``` **Example: A sync that requires multiple actions to happen**. In this sync, when a user successfully registers a new account and successfully creates a new profile with a given display name, a notification is sent whose content is a message that says that the user with the given display name has been registered: ``` sync AccountRegistration when UserAuthentication.register (): (user) UserDisplaying.newProfile (user, displayName) where msg = displayName + 'is registered' then Notifying.notify (user, msg) ``` Note that in syncs arguments of actions are bound by name, so arguments that are not relevant to the sync can be omitted. In this case, the username and password provided to the `register` action are not relevant and so have been omitted. When a variable `var` appears in an action occurrence in a sync, its meaning is that the argument with the name `var` has been bound to the local sync variable also called `var`. If a different name is needed it can be provided explicitly, by writing `arg:var` to say that the argument `arg` is bound to the variables `var`. **Example: A sync with an argument bound to a variable of a different name**. In the previous example, in the call to the `notify` action of the `Notifying` concept, the action has two arguments, one called `user` (for the target of the notification) and one called `msg` (for the content). The declaration of the action in the concept definition might look like this ``` notify (user: User, msg: String): (notification: Notification) ``` The sync does not need to use the notification individual that is created by the action, so it does not appear in the sync. The sync passes the user that is bound to the variable `user` (which was returned by the register `action`) to the first argument of notify, and implicitly this variable is bound to the argument of the same name. The second argument likewise need not be named explicitly, since the where clause gives it the appropriate name. If however a different name were chosen in the where clause, the explicit form of binding a variable to an argument would be required: ``` sync AccountRegistration when UserAuthentication.register (): (user) UserDisplaying.newProfile (user, displayName) where message = displayName + 'is registered' then Notifying.notify (user, msg:message) ``` If a where clause produces multiple matches, the then clause of the sync fires on every match. This allows a simple way to express causal links in which one action leads to actions on a collection of individuals. **Example: Where clause with multiple matches**. Here is a sync to perform a cascading delete, so that when a post is deleted, all associated comments are deleted: ``` sync DeleteComments when Posting.delete (post) where Commenting._getByTarget (target: post) : (comment) then Commenting.delete (comment) ``` Note that the query `_getByTarget` takes a target and returns the *set* of comments on that target. The `comment` variable represents just one element of that set. So the effect of the sync is to call the `delete` action multiple times, once for each matching comment. Note also the use of the explicit argument naming here. Because the `Commenting` concept is not specialized to commenting on posts, the target of a comment is named a `target` in the argument of the query, not `post`. So the expression `target: post` says that the argument called `target` should be bound to the variable called `post`. Although a synchronization can include multiple actions in the *then* clause, this is rarely necessary. It is preferable for actions to be written in as granular a manner as possible, so that each causal link can be considered separately. This granularity also helps avoid complex iterations when multiple actions result from a single one. **Example: Granular synchronizations**. In a social media app, a sync could ensure that when a user account is deleted, all posts associated with that user are deleted too: ``` sync DeletePosts when Account.delete (user) where Posting._getByAuthor (user): post then Posting.delete (post) ``` There is no need to include in this sync the actions that delete each comment associated with each deleted post; that can be taken care of by another sync (the one we just saw).