Skip to content
wannacode

The enterprise landscape and capabilities

In short: A capability map describes what the business does regardless of its systems; linked to the applications it shows gaps and duplication, and the TIME model says what to invest in.

Problem

Three systems for invoices and none for returns

The shop has been growing for five years. The shop issues an invoice itself with each order. Accounting buys its own system and issues invoices for companies. Then marketing pays for an invoicing SaaS for the subscriptions, because "it was faster".

The finance director asks why invoices have three numbering schemes. Meanwhile customer service keeps returns in emails and a spreadsheet, and the accountant copies every return by hand.

Every team knows its own system. Nobody sees the whole.

The idea

What the business does, not which systems it has

Enterprise architecture looks at the whole company: all the systems, how they connect and what they are for. It starts not from the systems but from the capabilities: what the business does. It sells, invoices, delivers, takes returns. That changes rarely. Systems, departments and suppliers get replaced, and the shop still invoices ten years later.

A capability map arranges them in groups, usually on one page:

static readonly Group[] Map =
[
new("Sales", ["Catalogue", "Cart", "Orders"]),
new("Finance", ["Payments", "Invoicing", "Accounting"]),
new("Logistics", ["Warehouse", "Delivery", "Returns"]),
new("Customers", ["Notifications", "Support"]),
];
static readonly App[] Apps =
[
new("Shop", ["Catalogue", "Cart", "Orders", "Invoicing"]),
new("Warehouse 2009", ["Warehouse"]),
new("Accounting system", ["Accounting", "Invoicing"]),
new("Invoicing SaaS", ["Invoicing"]),
new("Payment provider", ["Payments"]),
new("Courier", ["Delivery"]),
new("Email service", ["Notifications"]),
new("Helpdesk", ["Support"]),
];
record Group(string Name, string[] Capabilities);
record App(string Name, string[] Capabilities);

Under the map you hang the applications: for each capability, which systems support it. Then two things show up that are otherwise invisible:

  • a gap: a capability with no system; people do it by hand;
  • duplication: a capability with several systems; you pay several times and have several versions of the truth.

See it

The map, coloured by coverage

// the systems that support the capability
static List<string> AppsFor(string cap,
IEnumerable<App> apps) =>
apps.Where(a => a.Capabilities.Contains(cap))
.Select(a => a.Name)
.ToList();
static string Coverage(List<string> names)
{
if (names.Count == 0)
return "✗ no system";
if (names.Count > 1)
return $"⚠ {names.Count} systems: "
+ string.Join(", ", names);
return names[0];
}
Step by step, in words
  1. The capability map: what the business does, in groups. No systems on it yet.
  2. The 8 applications come one by one. Each says which capabilities it supports, and their counts grow.
  3. A capability with exactly one system is covered: green.
  4. Invoicing: 3 systems do it. Three licences, three sets of invoice numbers, three places to fix.
  5. Returns: no system at all. People keep them in emails and spreadsheets.
  6. The map coloured by coverage: 1 gap and 1 duplication. The target state starts from here.
// the systems that support the capability
static List<string> AppsFor(string cap,
IEnumerable<App> apps) =>
apps.Where(a => a.Capabilities.Contains(cap))
.Select(a => a.Name)
.ToList();
static string Coverage(List<string> names)
{
if (names.Count == 0)
return "✗ no system";
if (names.Count > 1)
return $"⚠ {names.Count} systems: "
+ string.Join(", ", names);
return names[0];
}
static void PrintMap(IEnumerable<Group> map, IEnumerable<App> apps)
{
int gaps = 0, duplicated = 0;
foreach (var group in map)
{
Console.WriteLine(group.Name);
foreach (var capability in group.Capabilities)
{
var names = AppsFor(capability, apps);
if (names.Count == 0) gaps++;
if (names.Count > 1) duplicated++;
Console.WriteLine($" {capability}: {Coverage(names)}");
}
}
Console.WriteLine($"Gaps: {gaps}, duplicated: {duplicated}");
}
PrintMap(Map, Apps);
Output
Sales
Catalogue: Shop
Cart: Shop
Orders: Shop
Finance
Payments: Payment provider
Invoicing: ⚠ 3 systems: Shop, Accounting system, Invoicing SaaS
Accounting: Accounting system
Logistics
Warehouse: Warehouse 2009
Delivery: Courier
Returns: ✗ no system
Customers
Notifications: Email service
Support: Helpdesk
Gaps: 1, duplicated: 1
The map and the applications are arrays of records. We use arrays, not a Dictionary: the order of a Dictionary is in practice the insertion order, but it is not promised.

The program is small, but the real work is in the data: what counts as a separate capability, and what each system really does. You gather that by talking to the teams, not with code.

Try it

Fill in

How do you check that the application supports the capability?

apps.Where(a => a.Capabilities.(cap))

The idea

Application portfolio: TIME

The map says where the problem is. The application portfolio says what to do with each system. For each one you record what it costs per year and two scores from 1 to 5:

  • business value: how much the business needs it;
  • technical condition: how healthy it is inside (is it maintained, are there people who know it, how often does it go down).

The two scores give four decisions, known as the TIME model (Gartner):

technically healthy technically sick
valuable to the business Invest Migrate
of little value Tolerate Eliminate
record Rating(string App, int CostPerYear, int Business, int Technical);
// TIME: is it worth much to the business, and is it healthy inside
static string Decide(Rating r)
{
bool valuable = r.Business >= 3;
bool healthy = r.Technical >= 3;
if (valuable && healthy) return "invest";
if (valuable) return "migrate";
if (healthy) return "tolerate";
return "eliminate";
}

The prices below are examples, and the scores are the team's.

List<Rating> ratings =
[
new Rating("Shop", 60000, 5, 3),
new Rating("Warehouse 2009", 18000, 4, 1),
new Rating("Accounting system", 24000, 4, 4),
new Rating("Invoicing SaaS", 9600, 2, 2),
new Rating("Helpdesk", 4800, 2, 4),
];
int total = 0, saved = 0;
foreach (var r in ratings)
{
var decision = Decide(r);
Console.WriteLine($"{r.App}: {decision} (business {r.Business}, technical {r.Technical})");
total += r.CostPerYear;
if (decision == "eliminate") saved += r.CostPerYear;
}
Console.WriteLine($"Per year: {total} €, you drop {saved} €");
Output
Shop: invest (business 5, technical 3)
Warehouse 2009: migrate (business 4, technical 1)
Accounting system: invest (business 4, technical 4)
Invoicing SaaS: eliminate (business 2, technical 2)
Helpdesk: tolerate (business 2, technical 4)
Per year: 116400 €, you drop 9600 €

TIME is not a verdict but the start of a conversation. "Tolerate" means "do not spend on it", not "forget it": the Helpdesk works and is not in the way. "Eliminate" needs a date and a plan, or the application lives for five more years.

Try it

Predict the output

The mobile app brings half of the orders (business 4), but it is written on a framework that is no longer maintained (technical 2). What does decide say?

Console.WriteLine(Decide(new Rating("Mobile app", 30000, 4, 2)));
Answer

Try it

Find the bug

A colleague wrote decide with copy and paste. Now the portfolio says:

Output
Shop: invest (business 5, technical 3)
Warehouse 2009: invest (business 4, technical 1)
Accounting system: invest (business 4, technical 4)
Invoicing SaaS: eliminate (business 2, technical 2)
Helpdesk: eliminate (business 2, technical 4)
Per year: 116400 €, you drop 14400 €

Warehouse 2009 is suddenly up for investment and the Helpdesk is to be eliminated. Which line is the bug on?

Pick the line with the bug.

The idea

Target state and roadmap

The map and the portfolio give the target state: what the landscape should look like in a year or two. For the shop:

  • invoicing is only in the accounting system, with one numbering scheme; the invoicing SaaS goes (eliminate), and the invoice module in the shop is switched off;
  • the warehouse is replaced (migrate) with a new one that also supports returns: the gap is filled where the goods are;
  • everything else stays.

The roadmap is the order of steps to get there, with intermediate states that work:

  1. the new warehouse takes over returns, and the old one stays for the goods; the link to Warehouse 2009 goes through an anti-corruption layer, so its CSV does not leak into the new code;
  2. the goods move to the new warehouse gradually, as in "Changing a live system", until Warehouse 2009 has no traffic left;
  3. the shop's invoices move to the accounting system; the numbering changes from the new year, so it is clear for the accountants;
  4. the SaaS subscription is cancelled after the last invoice in it.

Each step is a separate decision with an ADR, its own cost and its own risks in the register. The order is chosen by pain and by dependencies: returns come first, because money is lost there today.

More precisely, if you are curious

The frameworks. TOGAF is the best known enterprise architecture framework: it describes a method (ADM) for these steps and many documents. ArchiMate is a language for diagrams of the landscape, and Zachman is a table for classifying the descriptions. Here we only name them: the course teaches the work, and certification is a separate path.

Capability, process and department. A capability is the "what" (invoicing). A process is the "how", in steps (order → invoice → payment). A department is the "who". The map is made of capabilities because they change the least.

The tools. Large companies keep the portfolio in dedicated EA tools or in a CMDB. A small company keeps it in a spreadsheet. What matters is that it is one place and it is kept up to date.

The idea

When it helps and when it is bureaucracy

Enterprise architecture helps when:

  • the systems number in the dozens and nobody keeps them all in their head;
  • the company merges with another and must decide which system stays;
  • software spending grows and nobody knows what it pays for;
  • a regulator asks where the customer data is.

It does harm when:

  • the map is the goal. Drawn, printed and forgotten. A map that no decision was ever made with is decoration.
  • a board approves every library. Decisions that one team can reverse by itself stay with the team. Only what is expensive for the whole company is decided centrally: shared identity, payments, customer data.
  • the company is small. Three systems and one team do not need a framework: a one-page table and ADRs are enough.
  • the framework replaces the conversation. The documents of the framework are not the work. The work is the decision that comes out of them.

Exercise

The target state

Solve this one in your editor.

Write the target state as a new list of applications: the shop no longer invoices, the invoicing SaaS is gone, and Warehouse 2009 is replaced by "New warehouse", which supports the warehouse and returns. Run the same coverage check on it:

Output
Sales
Catalogue: Shop
Cart: Shop
Orders: Shop
Finance
Payments: Payment provider
Invoicing: Accounting system
Accounting: Accounting system
Logistics
Warehouse: New warehouse
Delivery: Courier
Returns: New warehouse
Customers
Notifications: Email service
Support: Helpdesk
Gaps: 0, duplicated: 0

More exercises

  • A fitness function for the landscape. Turn the check into a test that fails if there is a gap or a duplication without a recorded decision (a list of allowed duplications and their reasons).
  • The cost of duplication. Add each application's cost and print how much all the systems of the duplicated capabilities cost per year.
  • Two countries. The shop launches in Greece with a local courier. Are two delivery systems a violation? How do you show it on the map?

Test

Check yourself

1. What is a capability?
2. A capability supported by three systems is…
3. Warehouse 2009 matters to the business (4) but is in bad technical shape (1). What does TIME say?
4. When is a capability map bureaucracy?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·