DEV Community

Doby Baxter
Doby Baxter

Posted on

When Is a System Not Fit for Purpose?

The question

When do we get to say that a system is not fit for purpose?

In software engineering, we usually have a fairly straightforward answer. A system is fit for purpose if it reliably does the thing it was designed to do.

That sounds obvious, but we seem to apply a different standard to public systems.

A government service can have a department behind it, an application portal, a helpline, a set of criteria, a budget, and hundreds of people working within it. It can process thousands of applications and schedule thousands of appointments.

None of that necessarily tells us whether it is actually achieving its purpose.

A system existing is not the same thing as a system working.

And from a developer's perspective, that distinction matters.

Activity is not an outcome

Imagine an application with a perfectly functioning workflow:

User
  ↓
Application
  ↓
Validation
  ↓
Assessment
  ↓
Decision
Enter fullscreen mode Exit fullscreen mode

Every component works.

The application is submitted successfully.

The validation passes.

The assessment takes place.

The decision is recorded.

From an operational perspective, the system has performed exactly as expected.

But what if the user finishes the process without receiving the support they actually needed?

The system has produced activity, but not an outcome.

This is a familiar problem in software engineering. We can build dashboards showing requests processed, jobs completed, transactions handled, and uptime maintained, but those metrics don't necessarily tell us whether the system is solving the problem it was built to solve.

Applications received are activity.

Appointments attended are activity.

Cases opened are activity.

Forms processed are activity.

The more important question is:

Did the user's situation actually change?

That is an outcome.

And when a system measures activity more effectively than it measures outcomes, it becomes possible for the system to look healthy while the people using it experience failure.

The edge-case problem

One of the first things you learn as a developer is that the happy path isn't the whole system.

You can design a beautiful workflow for:

Input → Processing → Success
Enter fullscreen mode Exit fullscreen mode

and still have a terrible system if you haven't thought carefully about what happens when the input doesn't fit your assumptions.

Real systems encounter:

  • invalid input
  • missing information
  • unsupported configurations
  • unavailable dependencies
  • unexpected states
  • conflicting requirements
  • users who don't behave exactly as expected

Good software doesn't pretend those cases don't exist.

It detects them.

It explains them.

And, where possible, it provides a recovery path.

Public services have the same problem.

A system should be able to tell someone not only that they qualify, but also when it cannot provide what they need.

That isn't necessarily a failure.

In many cases, early rejection is better system behavior than indefinite processing.

If an API knows that it cannot fulfill a request, returning a clear error is preferable to accepting the request, passing it through ten other services, consuming resources, and eventually failing without explaining why.

The same principle applies to services involving people.

If a program cannot realistically help someone, keeping that person inside the process for months isn't evidence that the program is working.

It may simply mean the system doesn't have a meaningful failure state.

What a good failure state looks like

Consider two systems.

System A

A person enters a service.

The system determines that it cannot help them.

It explains why and provides an appropriate onward route.

Person
  ↓
Assessment
  ↓
Not suitable
  ↓
Explanation
  ↓
Alternative service
Enter fullscreen mode Exit fullscreen mode

The person hasn't received the original service, but the system has still produced a useful outcome.

Now consider System B:

Person
  ↓
Application
  ↓
Appointment
  ↓
Documentation
  ↓
Follow-up
  ↓
More documentation
  ↓
More appointments
  ↓
Eventually: cannot help
Enter fullscreen mode Exit fullscreen mode

System B may produce considerably more measurable activity.

But it is arguably the worse system.

From an engineering perspective, the difference is obvious.

System A has a defined failure state. System B has an expensive failure path.

The user spends time and resources discovering something the system could potentially have established at the beginning.

That's not just inefficient.

It's poor system design.

The infinite loop

There is another failure mode that becomes particularly interesting when you look at public services as systems: the infinite loop.

Suppose a program is intended to help bridge the gap between a candidate and an employer.

The intended architecture might look something like this:

Candidate
   ↓
Program
   ↓
Employer connection
   ↓
Employer engagement
   ↓
Employment
Enter fullscreen mode Exit fullscreen mode

But what happens if the program can only meaningfully intervene after an employer has already engaged?

The process becomes:

Candidate applies
   ↓
Employer does not engage
   ↓
Application rejected / ignored
   ↓
No employment
   ↓
No employer-side intervention
   ↓
Candidate applies again
   ↓
Repeat
Enter fullscreen mode Exit fullscreen mode

Nothing has technically crashed.

There may be no obvious software equivalent of an exception.

The system is simply unable to transition into the state required for success.

In software terms, I would immediately ask:

What state transition is missing?

If the purpose of the system is to help create an employer connection, but the system depends on an employer connection already existing before it can provide that intervention, then there is a circular dependency.

The system is waiting for the event it is supposed to help create.

That deserves architectural scrutiny.

Every system has boundaries

This is also where transparency becomes part of system design.

Every system has a boundary.

An API has supported inputs.

A database has constraints.

A service has capacity.

A platform has supported environments.

Good systems document those boundaries.

You don't want a developer discovering after six months that an API simply cannot support the operation they have been building around.

The same should be true for services involving people.

If a program only has established employer relationships in particular sectors, that matters.

If certain types of work are outside its scope, that matters.

If particular circumstances mean the service is unlikely to produce a useful outcome, that matters.

The user needs that information early enough to make decisions about their own time.

Otherwise, the system effectively hides its own limitations and leaves the user to discover them through repeated failure.

Observability matters

Modern software systems rely heavily on observability.

We want to know:

  • what happened,
  • when it happened,
  • where it happened,
  • why it happened,
  • how long it took,
  • where the failure occurred,
  • and what happened afterward.

Without observability, debugging becomes guesswork.

The same principle should apply to public systems.

If a program claims to connect people with employers, we should be able to answer questions such as:

  • Which employers have established relationships with the program?
  • Which sectors are covered?
  • How many participants were successfully connected through those relationships?
  • How many were simply advised to submit standard applications?
  • How many cases were identified as unsuitable?
  • What happened to those people afterward?
  • How many received an effective onward referral?

These aren't hostile questions.

They're essentially observability questions.

If the system cannot answer them, then it becomes difficult to determine whether the system is performing the function it was designed to perform.

Metrics can create the wrong behavior

This is another familiar problem in software engineering.

What you measure influences what you optimize for.

If an engineering team is measured primarily on the number of tickets closed, engineers will naturally become better at closing tickets.

That doesn't necessarily mean the product is improving.

If a call center is measured primarily on call duration, reducing call duration becomes an obvious optimization target.

That doesn't necessarily mean customers are getting their problems solved.

The same problem can exist in public services.

If success is primarily represented through:

  • applications,
  • appointments,
  • interactions,
  • cases,
  • referrals,
  • and completed processes,

then the organization can become extremely good at generating activity.

But activity isn't necessarily progress.

The difficult metric is the one that matters most:

What changed for the person?

Did they get employment?

Did they receive the support they needed?

Did the barrier actually get removed?

Did they reach another service that could help them?

Those are outcome metrics.

And outcomes are often considerably harder to measure than activity.

The user becomes part of the error-handling layer

There is another cost that doesn't always appear in system metrics.

When a system fails, somebody has to compensate for the failure.

In software, that might mean a retry mechanism, a dead-letter queue, a fallback service, or an operator intervention.

In poorly designed human-facing systems, that responsibility can quietly move onto the user.

A form fails, so they try again.

An email goes unanswered, so they follow up.

A verbal commitment isn't recorded, so they document it themselves.

A case is transferred, so they reconstruct the history.

A process doesn't explain what is happening, so they maintain their own timeline.

Eventually, the person becomes the system's external state store.

They keep the records.

They track the state.

They reproduce missing context.

They provide the observability the system itself failed to provide.

From a developer's perspective, that's a serious design smell.

The system has outsourced state management to the user.

Documentation becomes infrastructure

This is why documenting every interaction can become so important.

Emails, dates, appointments, names, decisions, promises, and supporting documents effectively become a second system running alongside the official one.

The user maintains it because the official system may not reliably preserve the context they need.

Imagine having to manually maintain your own audit log because a production system doesn't provide one.

You would consider that an architectural problem.

Yet people navigating complicated administrative systems can end up doing exactly this.

They become responsible for preserving the state of their own case.

That work has a cost.

It takes time.

It takes attention.

It takes organization.

It takes emotional energy.

And none of those costs necessarily appear in the institution's metrics.

Accessibility is part of system design

This becomes even more important when the system is intended to support disabled people.

A system that requires sustained attention, precise documentation, repeated communication, and extensive administrative work may unintentionally create barriers for the very people it is intended to support.

That doesn't necessarily mean every requirement should be removed.

It means the system needs to account for the users who have to operate it.

In software, we call this accessibility.

We don't expect every user to interact with a system in exactly the same way. We provide alternatives, reduce unnecessary friction, and design interfaces around the capabilities and needs of the people using them.

Human services should be treated with the same principle.

If completing the process requires overcoming the same kind of barrier that the service is supposed to help with, then the architecture deserves examination.

You shouldn't need the outcome in order to have the capacity to navigate the process that produces the outcome.

Failure should be visible

A well-designed system makes failure observable.

If a dependency is unavailable, the system should say so.

If an input is invalid, it should explain why.

If a request cannot be fulfilled, it should fail clearly.

And if the system itself cannot provide the required service, that limitation should be visible as early as possible.

The alternative is a system where failure is hidden behind continued activity.

The user keeps attending appointments.

More forms are submitted.

More emails are exchanged.

The case remains open.

The process continues.

Eventually, months later, the system reaches the same conclusion it could potentially have reached at the beginning.

From the organization's perspective, there was plenty of activity.

From the user's perspective, there was no progress.

So, when is a system not fit for purpose?

I don't think the answer is simply "when the system isn't perfect."

No serious system is perfect.

And I don't think saying no is inherently a failure.

Sometimes the correct system behavior is:

We cannot help you with this.

The important part is what happens next.

A system becomes questionable when it cannot identify its own boundaries, cannot communicate them clearly, and can continue consuming a person's time and resources without producing a meaningful outcome.

In software, we'd investigate that.

We'd look at the architecture.

We'd examine the state transitions.

We'd inspect the failure modes.

We'd check the metrics.

We'd look at the monitoring.

And we'd ask whether the system is actually optimizing for the thing that matters.

Public services deserve the same level of scrutiny.

Because the ultimate test isn't whether the process ran.

It's whether the system achieved its purpose.

A system can be operational, compliant, fully staffed, and processing thousands of cases while still not be fit for purpose.

Sometimes the most important failure isn't that the system crashed.

It's that the system kept running exactly as designed while the person it was supposed to help went nowhere.

Top comments (0)