Skip to content
UdriaUdria

Library · Opinion

The Software Capability Is Becoming a New Unit of Development

Libraries reuse code. Coding agents may let us reuse something larger: complete implementation knowledge.

By Roman Grigoriev13 min read

Software has always advanced through reuse.

We stopped writing everything in assembly.
We stopped implementing our own databases.
We import libraries.
We call APIs.
We use frameworks.
We buy managed infrastructure.

Each step changes the boundary between what a team creates and what it inherits.

Coding agents may create another useful boundary:

the software capability.

Not a package containing one function.

Not an entire application.

A reusable description of a production feature — architecture, behaviour, edge cases and tests — that an agent can adapt to the application that already exists.

This article argues that the unit is genuinely new, describes what one contains in enough detail to be worth disagreeing with, and is explicit about where the idea is currently weakest.

Software capability: a reusable package of domain behaviour, architecture, constraints, edge cases and tests that a coding agent can adapt into an existing application, where the resulting implementation belongs to the customer rather than to the vendor.

That is how Udria uses the term. It is not an established industry definition, and other people use the word "capability" to mean other things — a platform feature, a maturity level, a team's competence. Nothing here depends on the word itself. What matters is whether the unit it names turns out to be a useful boundary for reuse.

Why libraries are not enough

Libraries are excellent when the reusable boundary is technical and narrow.

For example:

parse this date
validate this schema
sign this token
render this component

The library owns an interface.

The application owns the surrounding behaviour.

But many common SaaS features do not fit neatly behind one API.

Take role-based permissions.

It touches:

  • users;
  • organisations;
  • database schema;
  • middleware;
  • API handlers;
  • UI;
  • invitations;
  • audit trails;
  • tests.

There may be useful libraries inside that feature.

The feature itself is a cross-cutting capability.

The distinction matters because of where the hard parts live. A permissions library can give you a correct can(user, action, resource) function. It cannot tell you that the last owner of an organisation must not be able to remove themselves, that a revoked session should stop being authorised before its natural expiry, or that an invitation accepted twice must not produce two memberships.

Those are not interface problems. They are decisions about behaviour, and they are the ones that produce incidents.

Why complete products are too much

At the other extreme, you can buy a complete product.

That is often sensible.

But sometimes the requirement is:

I want this capability inside my product, using my users, my organisations, my interface and my database.

A separate SaaS tool may create:

  • another runtime dependency;
  • another UI;
  • another identity model;
  • another data boundary.

The team may prefer to own the resulting implementation.

Historically, that ownership implied doing much more implementation work internally.

Coding agents change that trade-off.

Why boilerplates are different

Boilerplates package a lot of working code.

They are especially useful when starting from zero.

They say:

Here is an application architecture with common features already implemented.

That is valuable, and for a greenfield project it is often the right answer — an argument made at more length in Build, Buy, Boilerplate or Ask the Agent?.

But an existing product cannot casually become a different repository.

You need adaptation.

A capability says:

Here is how this feature should behave. Here are the difficult decisions and tests. Now inspect this repository and implement it in the local architecture.

The coding agent becomes the adaptation layer.

What a capability is not

Four things it gets mistaken for, each with a real difference.

Not a code generator. A generator emits code from a template and parameters; the output is determined before it sees your repository. A capability is read, not run, and the code it produces depends on what the agent finds.

Not a plugin. A plugin executes inside a host at runtime and remains the vendor's code. Capability output is your code, in your repository, with no vendor component in the request path.

Not an SDK. An SDK gives you an interface to somebody else's system. A capability describes behaviour that will live entirely inside yours.

Not a prompt. This is the closest one, and the distinction is the tests. A prompt communicates intent and stops there. A capability carries executable criteria that can disagree with the implementation afterwards — which is the whole point, because intent is what everybody already has.

Where it sits among the other units

Unit What you receive Where it runs How a fix reaches you If the vendor disappears
Library An interface and its implementation Your process, vendor's code Version bump Pinned version keeps working; no more fixes
Framework Structure and conventions Your process, vendor's code Version bump, sometimes a migration You are on an unmaintained framework
Boilerplate A whole starting repository Yours It does not; you diverged on day one No effect, you own it
SaaS product A running service Vendor's infrastructure Automatically, whether you wanted it or not Your feature stops
Capability Behaviour, constraints and tests Yours Unsolved — see below No effect, you own it

The last two columns are the interesting ones, and they trade against each other. Everything that makes capability output durable — it is yours, it runs on your infrastructure, it survives the vendor — is the same property that makes updates hard to deliver. You cannot have code nobody can take away from you and code somebody else can silently fix.

What belongs in a capability?

The useful contents depend on the feature. Rather than list the sections abstractly, it is more honest to work one through.

Take team invitations. It is a good example precisely because it sounds trivial. Almost every B2B product has it, almost nobody considers it interesting, and almost everybody gets some part of it wrong.

Domain model

Entities, states and relationships.

An invitation is not a row with an email address on it. It is a small state machine:

pending → accepted
pending → revoked
pending → expired

And it has an identity problem baked in: an invitation is addressed to an email, but membership belongs to a user account, and those are not the same thing until somebody signs up.

Invariants

Properties that should always remain true.

an accepted invitation produces exactly one membership
a revoked invitation can never be accepted
an expired invitation can never be accepted
a user cannot hold two memberships of one organisation
the role granted is the role that was offered, not the role
  the inviter holds at acceptance time

That last one is the sort of thing that never appears in a ticket. If an admin invites somebody as a member, is demoted, and the invitation is accepted a week later, what role does the new person get? There is a right answer, and it is not "whatever the code happens to do".

Architecture

Where responsibilities should live.

Accepting an invitation changes membership, which is an authorisation fact. That means it belongs on the server, in a transaction, behind a check that the accepting user's email actually matches the invitation — not in a client handler that trusts an invitation ID from a URL.

Edge cases

Failure and boundary conditions that are easy to miss.

  • The invited email already belongs to a member.
  • The invitation is accepted by a signed-in user with a different email.
  • Two invitations exist for the same email at different roles.
  • The invitation link is clicked twice, quickly, in two tabs.
  • The organisation is deleted while an invitation is pending.
  • The inviter loses the right to invite before acceptance.
  • The email bounced, so the invitation exists and nobody knows.

Each of these has a defensible answer. None of them is discoverable by reading the happy path.

Security constraints

Trust boundaries and privileged operations.

Invitation tokens are bearer credentials that arrive over email. They need entropy, expiry, single use, and no useful information leaked in the URL. An invitation that grants owner rights deserves different handling from one that grants read access.

Implementation sequence

A safe order for modifying an existing application.

Schema and constraints first, then server-side acceptance, then the sending path, then the UI. Doing it in the other order produces a working demo with no enforcement underneath it, which is the state most implementations are actually in.

Acceptance tests

Executable evidence for the required behaviour.

accepting twice produces one membership
a revoked invitation cannot be accepted
acceptance by a mismatched email is refused
two concurrent acceptances produce one membership
role at acceptance matches role at invitation

The last two are the ones that distinguish a specification from a description. Both fail against a naive implementation.

Compatibility guidance

Which stack assumptions exist and where adaptation is expected.

That is what one capability contains. None of it is code that can be copied — all of it is knowledge that determines whether the code is right.

The final code is still written locally.

The agent is what makes this practical

Before coding agents, distributing a detailed capability specification without implementation code could feel incomplete.

Someone still had to translate it into:

  • migrations;
  • functions;
  • routes;
  • UI;
  • tests.

Now that translation is exactly what coding agents are increasingly good at.

They can inspect local conventions and perform mechanical adaptation.

That makes reusable knowledge more valuable.

It also changes what the reusable artefact should look like. A library has to guess at the shape of the application it will be dropped into, so it exposes configuration for every variation it can imagine. A capability does not have to guess, because the agent reads the actual repository. The specification can therefore afford to be opinionated about behaviour while being silent about naming, structure and framework.

This changes the economics of niche software

Consider a feature that is too small to justify buying a separate SaaS product but too complicated to want to rebuild carelessly.

There are thousands of these.

Examples:

  • audit logs;
  • invitation systems;
  • approval workflows;
  • feature entitlements;
  • scheduling;
  • signing workflows;
  • import pipelines;
  • team permissions.

A traditional vendor might need to host and operate an entire service to monetise one of these problems.

A capability vendor can instead sell the implementation knowledge while the customer's own stack hosts the resulting code.

The customer pays its own infrastructure and coding-agent compute.

That opens a different product shape — and a much lower floor for what is worth productising at all. A problem that would never support a hosted service with a support rota and an uptime commitment can still support a specification and a test suite.

Capabilities can become progressively specialised

The idea becomes particularly interesting once the basic category exists.

You can imagine:

billing/subscriptions
billing/usage
permissions/rbac
permissions/custom-roles
scheduling/availability
scheduling/resources
signing/document-workflow
crm/deals

Some capabilities are broad.

Some are highly specific.

The coding agent composes them with the application.

How the knowledge would compound — and where that breaks

The argument for a capability improving over time is easy to state.

A customer finds an edge case. A test is added. The specification is corrected. The next buyer inherits the fix. Version 1.1 is better than 1.0 because somebody's production incident paid for the difference.

That is how libraries improve, and it is a real advantage over every team independently rediscovering the same failure.

But there is an honest problem in the middle of it, and it is worth stating plainly rather than discovering later.

A capability vendor cannot ship a fix to code it does not own.

When a library publishes a patch, you bump a version and the fix arrives. When a capability publishes a correction, the affected code is already inside a hundred different repositories, adapted differently in each one, possibly edited since. There is no npm update for a decision somebody else's agent implemented three months ago.

The plausible answers are not obviously good enough yet:

  • ship the change as a described diff and let each customer's agent apply it to their own adaptation;
  • ship the acceptance test alone, so a customer can at least discover whether they have the defect;
  • version the specification and let customers re-run the capability against their repository.

The second is the most interesting, because it is the one that degrades gracefully. A test that fails tells you something true about your code even if the suggested fix does not apply to your architecture.

This is an unsolved part of the model, not a solved one. Anyone evaluating the idea should ask about it.

Where the capability model fails

A category argument that only lists advantages is marketing. These are the conditions under which this one does not work.

The repository is too unusual. Adaptation assumes the agent can recognise the local conventions. A codebase with an idiosyncratic data layer, heavy metaprogramming or no coherent structure gives it very little to map onto.

The agent adapts confidently and wrongly. Mechanical translation is exactly where agents are strong, but a subtly wrong mapping — the wrong table treated as the tenant boundary, say — produces code that looks correct and passes the obvious tests. This is why the acceptance suite matters more than the prose: a specification a human reads is checked by attention, and a test is checked by the machine.

The tests pass and the behaviour is still wrong. Acceptance tests encode the behaviours somebody thought of. They cannot encode the ones nobody has hit yet. A capability that has been through more scrutiny than a first-pass prompt is a real claim; "every edge case is covered" is not, and no serious vendor should make it.

The knowledge rots. Specifications describe vendor behaviour, and vendors change. A billing capability written against one API version is a liability once that version moves. Capability knowledge needs maintenance in a way that a frozen boilerplate does not — which is a cost, not a feature.

The capability is too large to be read usefully. A specification detailed enough to be worth buying may be too long for an agent to hold alongside the repository it is adapting into. That is a real constraint on the format, not a detail: a capability has to be structured so that the preflight, the domain model, the individual edge cases and the tests can be read separately and in the order the work needs them. A single long document that has to be consumed whole is the wrong shape, however good its contents.

The feature was never actually common. The whole argument depends on the problem recurring. Where a requirement turns out to be genuinely specific to one business, reusing somebody else's decisions is worse than making your own.

None of these kills the idea. All of them bound it.

This does not remove engineering judgement

A reusable capability cannot know every product requirement.

A team still needs to decide:

  • whether the capability fits;
  • which optional behaviours it wants;
  • which local conventions matter;
  • which risks deserve review.

The agent should not blindly install software into a sensitive system.

The value is narrower:

Stop rediscovering common implementation knowledge when the problem itself is common.

That is a form of leverage software has always pursued.

Coding agents simply make a larger unit of reuse practical.

What would show that this is real

The claim is testable, which is the useful property of it.

If the capability is a genuine unit of reuse, then giving an agent the specification and the acceptance suite should measurably beat giving the same agent the same task from a blank page — measured not in tokens but in how close each result gets to a production definition of done, and how much human attention each one consumes. That comparison, and how we intend to run it, is set out in You Can Ask Claude to Build It. The Question Is Why You Should Have To.

If the difference turns out to be small, the honest conclusion is that agents have already absorbed enough production knowledge that packaging it separately adds little. That is a result worth having.

And it connects to the broader question of what evidence should make anyone willing to ship generated code at all, which is the subject of What Does Production-Ready Mean When AI Writes the Code?

The interesting question is what becomes reusable next

As implementation becomes cheaper, we think the boundary of software reuse will move upward.

From functions.

To libraries.

To frameworks.

To services.

And now, potentially, to complete capabilities that retain local ownership while reusing accumulated engineering knowledge.

That is the category Udria is exploring.

Sources

Was this useful?

Related reading