For the past decade, I have been developing business systems (specifically supply chain) and continue to face the same challenges when creating, supporting, or enhancing systems. These challenges are often less complicated than they initially seem – it’s all about remembering how we’ve approached these in the past.
In order to support good design practice, long term system life expectancies and agility, I’ve come up with my own personal set of guiding principles that I’d like to share with you.
1. It’s very unlikely that the problem you face hasn’t been solved before
Electronic Data Interchange (EDI) is a great example of a largely but not exclusively supply chain industry communication standard. EDI as an integration medium, includes a deeply refined collection of specifications, each including elements designed to solve almost every known integration problem between trading partners. This medium has been polished through sheer time and use by carriers, vendors, and distributors alike.
Deviating from this medium is inevitable as new technologies form, but we can’t forget the lessons an entire industry has learned and baked into the EDI lifecycle. When implementing new kinds of solutions for already-solved industry problems, we need to remember to revisit what we’ve done in the past in order to remind ourselves of the ‘whys behind the hows’, while leaving ourselves to reinvent the how in order to remain free and creative.
2. Abstraction is your friend
We’ve all at some point or another built a system coupled to a very narrow view of a given thing. Here’s some examples to get you thinking:
A shipment has an assigned truck/trailer #
A freight payment system pays freight
3. A store is a store and will be nothing else, ever.
4. “We need to archive price files we’ve sent our partner over time”
If we look at the above problems in more generic and abstract terms, we can turn them into the following problems instead:
3. Don’t assume you’ll never have more than one implementation of that thing
- A shipment has several different classifications of ‘label’. When we instead imagine this as a ‘label’ we can also start to imagine that each label has its own distinct lifecycle too. For example: a trailer can be pulled by multiple trucks.
- A freight payment system probably pays anything that supports the shipment of freight and not just freight itself. For example: carrier charges, equipment repair costs, and import/export fees).
- A store is nothing more than a facility that facilitates retail sales (today). This distinction opens up new possibilities for using this facility as a remote distributor in your supply chain network, without having to completely rework your system or shoehorn the idea in.
- “We need to retain a history of what we’ve quoted our partner over time”. This distinct way of thinking opens up possibilities and creates a system that doesn’t block new ways of using the existing system. This leaves your system requiring only extension as opposed to a fundamental refactor.
Too often we’ll assume that a certain kind of input will only ever come from a single source of record or producer. Similarly, we’ll assume that only one instance of a given entity will exist in our system.
Hindsight is 20/20 in this matter. Critically look at systems you’ve developed in the past and ask yourself “could you have had more than one of ‘that thing’”?
Here are examples of items in which I’ve seen systems coupled to one implementation of:
- A single external pricing engine or source of pricing
- This creates an integration challenge when (for example) you’re receiving a declared price from a wholesale partner
- A single external source of demographic/HR information
- This creates a challenge when integrating an additional workforce defined by an external business (e.g. an employment agency)
- A single call centre
- This creates a challenge when adding additional tenants
- A single source of order information
- This creates a coupling to your source system, resulting in a more monolithic system that you realized you had.
- A single business banner/subsidiary
- This creates a challenge in the event your organization absorbs and integrates another smaller but still somewhat segregated company. (e.g. separate product catalogues or workforce)
Assuming you’ll ever have one of the above items creates a series of barriers in expanding your system. In order to adhere to Agile practices you probably don’t want to future-proof too much. However, as someone who has had to shoehorn multi-tenancy and multi-banner support into an existing production system, I would challenge that there is a balance that must be met in order to remain agile.
You must not subconsciously create roadblocks that prevent your business from pivoting at the moment it needs to pivot.
4. Reminder: Your system inputs and outputs are actually two distinct things
Systems are often built to ‘process data and produce data’. This is expected as this is pretty much the definition of a system. However, systems are often incorrectly developed to instead ‘process and update the data’. There’s a distinction here I need to call out…
Below is an example of load data being processed by a KPI processor, then updated. Keeping the input and output in the same place makes it really easy to grab what you need for reporting and analysis, however…
…as soon as you add another input, you’re forced to store your output back with your two inputs. This complicates your reporting and analysis as your important KPI data is now in two places.
What happens if you require your system to produce another version of the same KPI? Maybe one with adjusted specifications as your shipping department has a different idea of the truth than your dispatch department. Below is an example of that second processor shoehorned in… Not pretty, right?
What happens when you separate the output from the input? You can see at least a few benefits:
- You can keep adding versioned or outright new data processors and result storage without decentralizing the storage of your processor outputs.
- As you create storage for your processor outputs, your domain model remains untouched.
- You can add new data inputs to your existing processors without having to couple the outputs to that specific implementation of input.
A system (or in this case, a subsystem) is comprised of inputs, processes, and outputs. To consolidate 66% of what makes a system is to implement an anti-pattern.
5. Create an opportunity for comfort, not the requirement for documentation
The goal: Your support team, systems analysts, developers, or anyone for that matter need to understand what your system is doing and on-demand. You think documentation is the answer, however, it isn’t. Documentation is full of old truths. In reality, you need to accept that your code is your documentation and your team needs to learn how to read it.
What do I suggest you do? Remove the fear and enable your team. Get them access to the code. Help them achieve the know-how to navigate it and read it quickly. This might require formal training at a cost, or you may have to be a role model.
In summary, good quality code is a must-have. First, simply make your system easy to mentally navigate by following the previously discussed guidelines. You’ll notice that these guidelines don’t actually include much technical jargon. A well-built and well thought out system is a business oriented system. The critical first step in building a business oriented system is to think about your business system in abstract terms.
Try asking yourself this question: “How would this work if I used carrier pigeons?”
The physical implementation is largely unimportant, thus you need to deeply understand the abstract processes, flow of information, and actual/potential business function of each entity. Once you do this a few times, it’ll become second nature.