All business software automates some analogous real-world process—even if that process is no longer visible. Software is not a means to its own ends. Good design comes from tying these things together.
Writing software for a business is hard work. Typing code is not the hard part.
The disconnect between these two ideas sits in the realm of The Business. Writing software for the business is much harder than mere coding because it takes time, patience, and communication to understand what the business needs. Often times, the business does not have a concrete understanding of its needs: perhaps the knowledgeable people are no longer around, or the needs are emergent and not yet well-understood, or perhaps those parts of the business have already been automated in software for which the theory is lost.
Software that is written without a critical understanding of the intentions of the business will go its own direction. It might work “correctly” against its own understanding, but customers will report bugs. They’ll express dissatisfaction. Internal stakeholders will pressure teams to change priorities to fix friction caused in business processes.
It’s paramount that we understand that software automates a business process. If we don’t understand how that business process works—how internal stakeholders interact with it, how customers are supposed to engage it, what its role is in relation to revenue—then we are doomed to automate it imperfectly.
There is no technical solution to these problems. They are entirely social.
I have a proposal for you on how to write software more thoughtfully, however, whereby we’ll inadvertently help ourselves with these problems—and a host of other problems!
First, some examples.
Let’s start by testing out that assertion—that software automates the business—to get into the right head space. Here are several software components and what they look like in the real world.
- Websites: A website is automated communication and activity. Rather than calling a business for information or going to it to perform an activity, we can do this online.
- Webserver routers: The conversation a user has with a router goes something like “I’d like to know about X” (via the URL their browser submits to the router), which is then directed to the subsystem that knows best how to answer that inquiry.
- Databases: Businesses have always needed to store data about their customers, products, interactions, and operations. Even today, they fill books and filing cabinets with this information.
- Indexes: Databases use indexes to speed up how they locate information. People create these artifacts for navigating the physical world, too. Before computers, libraries used card catalogues to list books and topics so tomes of interest might be located in the index before retrieval. Without this, finding the right book by handling all of the physical books would have taken significantly longer.
- Model: In MVC, a model represents a thing or concept in reality. It might be analagous to that literal thing in reality, or it could represent the mental structure of a thing as it would manifest on, say, a paper form.
- View: Views are an automation of a physical interaction. A view is how a person interacts with another person, process, or construct.
- Controller: MVC controllers translate user activity into business logic activity and data. They automate things in the real world such as jobs, rules, policies, and processes.
- Encryption: Secure communication has always been important to people. These days, we have effective algorithms for securing communication—for timeliness’ sake, they’re really only tenable with a computer, but one could imagine communicating using TLS on paper if time weren’t a problem!
The Cabinet Method for designing good software.
The crux of the Cabinet Method is the understanding of how your business would implement a process if computers did not exist (and if many of the constraints that computers solve for, like time, also did not exist). By understanding the fundamental process, we can write much higher-fidelity software which will be easier to maintain as requirements and constraints change.
It’s done in four steps:
- Understand the business
- Write down a description of the process
- Translate into software
- Implement
Step 1: Understand the business.
Programming shouldn’t begin until the business’ needs are truly understood. A terrifically incomplete list of things to understand first includes:
- Who are the beneficiaries of the software?
- What is the customer attempting to do?
- How do they view their situation when they come to engage with this feature?
- What problems or questions will the support team need to account for once people are interacting with the feature?
- Which of our employees are taking which actions to turn the results of this software into revenue?
- Who needs what information to do further planning and reporting on the process and results?
Product managers are typically responsible for gathering this information in the form of requirements. They come with their own biases and depth of understanding, though. Their requirements are not the automated result. Everyone involved in the project needs to have a deep understanding of the business process at hand, and has the obligation to push for more information (or to seek it out directly) if the level of understanding is not high enough.
Example: ACMEcorp needs records about its customers so marketing and sales can contact them and so we know how to direct transactional communications as we work together with the customer on their equipment purchases. Basically, we need a registration system.
Step 2: Write down a description of the process.
A comprehensive view of the business process needs to be written down. It needs to include the answers to the above questions, laid out in a natural narrative format that shows where the process is initiated and where its results go.
This is where the real trick is: It should be written in terms of human interactions and physical storage alone.
Pretend computers don’t exist. Invent job titles, roles, departments, names of paper forms as they relate. Think through how all of these things interact with each other sequentially to enact the business process from start to finish.
Example: A new customer arrives at an ACMEcorp outlet. They saw the new advertising we put out and are interested in getting our help on new equipment purchases. A lot of the details about our vendors’ equipment is confidential, and our legal team requires consent from the customer before we can share much with them. The front desk gives them a form which collects their contact information. Along with the form is our Terms of Service, which we ask them to sign before giving them access to our brochures. The customer can fill these out and hand them back to the front desk. The Customer Data Filing Manager takes these forms and files them into the Customers filing cabinet and the Agreements cabinet. They put a record identifier on the registration document, and note that identifier on the ToS document before filing both. As they do this, they also update the New Customers index document: the sales and marketing teams like to cohort their efforts by week, so next week they’re going to look at this registry to locate records for new customers so they can perform some welcome outreach. With registration out of the way, the customer is now free to peruse the equipment brochures and view confidential pricing information.
Step 3: Translate into software.
What’s the bare minimum software required to fulfill this story? What are the names of the existing systems involved in this process and how do they relate to the new requirements?
While going through this exercise, new constraints and processes will become evident. It can be interesting to continue updating the story with more people-and-paper-office analogies as these things are identified. Perhaps you use a central eventing design at your company, and you want to make a note about how the internal mailing department will need to deliver notes about new registrations to other interested departments.
This may also reveal inefficiencies and complexity in the current implementation. Is that abstraction really serving this story well enough that it’s worth bringing into the new design?
The most interesting part of this translation is often in how the database schema gets designed. Is that document always interesting to pull all at once, or is it more often interesting to look at one smaller section of the initial document? This can tell you whether you want wider database rows or more tables.
Do many departments want to request those documents? To fulfill which of their own specific business processes? Is it better for them to ask for the current document in its home department, or does it make more sense to deliver them their own copies and any subsequent updates so they can optimize their internal processes a bit better? Now we’re thinking about what we want our API interface to be, the possible role of evented systems, and the role of caching to some extent.
Step 4: Implement.
Go build the thing.
The business is the best thing to couple software to.
Software that is defined in terms of the business will be the most flexible for evolution.
The main way this is true is in reducing coupling between systems. If systems A & B both talk about the lead generation process in terms of how the sales team receives leads and how we make decisions about who to send over as a lead, then we have a system with an explicit decoupling point. Behind the interface which speaks about the named business process exists a translation into how that data is collected from the database and other systems. As the business evolves, that interface can receive updates to keep it aligned with reality. Things can be rethought without every underlying component needing to immediately start complying in the same way: I can rename something in my API contract but leave the old name in my database.
This can all be done very simply. We don’t need to bear the full cost of DDD’s approach to separate our interfaces from our implementations.
As the underlying implementations evolve, we can rewire them into the same interfaces if that part of the business process has not changed. If part of our lead generation logic requires me to check banking transaction data to ensure that a customer has spent more than $500 in the past month, then we have a business process of computing that data point from the transaction data and conveying it to interested parties. So long as we have an interface tailored for the business’ “lead generation cashflow data check” process, it does not matter if we’re working with Plaid or MX to gather that data. The cashflow team can change those partnerships and wire their new systems into that same business process—no client systems need to be updated to conform with the change.
Take, on the flipside, a system that dumps all of its database’s record update events onto Kafka for other unknown systems to interpret as they need. I don’t know who is coupling to my schema now. If I need to rename or remove a field in that schema, I either have to risk breaking a downstream client with that change, or I need to do significant work to identify and negotiate a migration with users. I might instead hack something in so that consumers still receive the old schema but I can migrate it without breaking them. Without designing this part carefully, this is probably going to get pretty nasty over time. Designing an event message that reflects what’s occurring in the business, though, instead of what’s defined in my database, means that I can reimplement any part of this system—or replace it altogether—without doing anything more than describing the current business process in the new implementation. Which is something that I’d end up needing to do anyway! It just ends up being easier and simpler this way.
Software can be simpler.
It should come as no surprise that coupling software to a lower volatility thing (your business) than a higher volatility thing (existing technical designs and partnerships) means that less maintenance is needed. Having all parts of the stack focusing on a clear-ish common thing (again, the business) instead of the fleeting specifics of implementation naturally means there is less coupling between things that are naturally prone to change.
This method is provided as an example of how to think through software design. There’s no reason to implement it as literally as I’ve written it. By following this avenue of thinking, your software will be more correct and more maintainable, all for the low cost of understanding the true needs of your business.