DEV Community

Cover image for Ownership Is a Field, Not an Agreement
Anton Brilliantov
Anton Brilliantov

Posted on

Ownership Is a Field, Not an Agreement

If the owner changes by a chat message and not by a commit, nobody owns the gates.


👋 Hi, I'm Anton - a software engineer working mostly in PHP/Symfony and Go, currently carving a live PHP monolith into Go services. Part 1 of this block was about a service card that is rendered from a declaration rather than typed by hand. This part takes one row of that card - the owner - and asks where that name is allowed to live. Running notes are on my GitHub: github.com/brilliant-almazov.

This is how I do it right now, with the price attached - maybe you already do it better, maybe you see it differently.


The path in one paragraph

A service here is raised from one declaration: the daemons it runs, the databases and queues and schedules it opens, whether it owns migrations. That file is not a description sitting next to the code - it is what the runtime and the deploy pipeline read, and the name of every resource environment variable follows from it by convention. Part 1 of this block is about that: a service card is a rendering of the declaration, not a page somebody keeps up to date.

So the question in this part is narrow. There are seven rows on that card. One of them is a human name. Where is it allowed to live?

The thesis, in one sentence

The owner of a service is a field in a declaration, and it changes by a commit.

A verbal agreement is not a declaration. You cannot show it to a new joiner, no build can check it, and six months later there is nothing to open. It exists exactly as long as two people remember the same version of it - and the day they remember different versions is the day it was needed.

The case: six steps, and a name on every one of them

The clearest thing I have that shows what "owner" actually means in practice is not an org chart. It is the chain a lead runs over a set of work.

The lead does not write code. The whole job is six steps:

  prepare the facts     ← the only place where reading the code is allowed
  hand the work out
  accept an iteration   ← against its own package
  accept the stage      ← one full run, once, at the end
  close the executors
  audit the set
Enter fullscreen mode Exit fullscreen mode

Three of those six steps are acceptance - two explicit ones and the audit that closes the set. There is nothing in the list about doing the work.

The constraint that goes with it: at most two executors on the main line and one on fixes. Fan-out - an executor per file, a batch per stage - is banned, because it burns limits for nothing. Fixes for an entire stage go to one executor as a single list, not one executor per remark. An executor that has finished is closed immediately.

Six steps of the lead - prepare the facts, hand out, accept an iteration, accept the stage, close the executors, audit the set - with the three acceptance steps marked, and a strip below showing the cap of two executors on the main line and one on fixes

That is what the word "owner" means here, and it is why it belongs in a declaration rather than in a conversation. The owner is whoever accepts. It is an executable role - a set of steps somebody performs - not a row in a table that gets filled in when a service is created and never read again.

How this is usually done

Three shapes, all of them reasonable, all of them common:

  • the owner in a wiki table - one page listing every service and a name next to it;
  • the owner in a ticket field - whoever the tracker assigns is who you go to;
  • the owner in a repository ownership file - the reviewer list that a pull request pulls in automatically.

All three work while somebody remembers them. None of them is tied to what actually deploys: the wiki page and the ticket field have no relationship at all to the artefact that ships, and the ownership file governs review, which is a different question from who accepts a release.

Where we put the field

Next to the service, in the same declaration the daemons and the resources come out of. That declaration is not documentation sitting beside the code - it is what raises the service. Owner and team are one of the seven rows of the card described in Part 1, and like every other row they are read from the declaration rather than typed somewhere else.

  service:
    name:   <service>
    owner:  <person>        ← the field this part is about
    team:   <team>
Enter fullscreen mode Exit fullscreen mode

Which means the field changes the same way everything else in that file changes: an edit, a review, a commit. It gets history, it gets a discussion attached to it, and "who owned this in March" is a question with an answer.

Two panels - on the left, an agreement: a chat line and three arrows going nowhere, marked not visible and not checkable; on the right, a field: the owner line of the declaration with an arrow down into commit, review, history

There is a second decision in the same family, and it is worth naming because it is the one that gets quietly taken by somebody else. The set of binaries a service runs is the owner's call. A new cmd/* is not something an executor introduces because a background job seemed to want its own process - new background work goes into a daemon that already exists. That is written as a prohibition in every control prompt, not left as something everyone is assumed to understand.

What the field is for: it holds the gates

The point of the field is not "who to blame". It is who holds the gates - and there are five of them between a wish and a release:

transition what is accepted what proves it
requirements → contract requirements are complete and consistent non-functionals stated as numbers, not as the word "fast"
contract → spec the contract is accepted the compatibility check is green
spec → execution the spec is executable without exploration no open question anywhere in the text
execution → acceptance the code does what it claims one acceptance command
acceptance → release the change does not break the neighbours build, tests, structure and drift checks

Five gate cards in a row - requirements to contract, contract to spec, spec to execution, execution to acceptance, acceptance to release - each with the artefact that proves it, under a strip reading the owner holds these

Two properties of that table matter more than the rows themselves.

The first row is the one that gets skipped, and skipping it is what makes the other four expensive: a requirement that says "it should be fast" cannot be accepted or refused, so it passes by default and turns into an argument three gates later, when there is code to throw away.

Between the gates, nobody intervenes. Watching work in progress is not control; it is noise with a manager attached. The control is at the transitions, and only there.

Whoever holds a gate may say "not accepted" without explaining it line by line. That sounds harsh written down, and it is the only version that works: accountability is delegated together with the decision. A gate that requires the holder to justify every refusal in detail is a gate anyone can argue their way through, which makes it decoration.

The one conclusion

A high share of the code here is written by a model executor. The accountability does not split in proportion. Configuring, checking and accepting stay with a person - and that is a technical position, not a moral one: an executor fails in a predictable set of ways, and the only reliable answer to a predictable failure is an external check rather than trust.

Which produces the requirement that every gate above is built on: a gate leans on an artefact - a number, a run, a file - not on a claim that it is done. "Finished, all green" is not an input to a gate. The output of the run is.

What it costs

Three costs, and none of them goes away with better tooling.

A field that changes by a commit changes slower than reality does. A person stops owning a service on a Tuesday; the field still carries their name on Friday, because changing it needs an edit and a review like everything else in that file. The lag is the price of the history.

There is no empty value. Every service needs a person who said yes. "The team" does not fit in the field - and the moment you allow it to, the field means "somebody", which is what it was introduced to stop.

That second one is worth sitting with. A field with no empty value is a small organisational demand with a large consequence: somebody has to agree, in writing, to be the person who says no. Most of the resistance I have seen to declaring ownership is not about tooling at all - it is that the declaration makes the acceptance explicit, and an implicit acceptance is more comfortable to hold.

It does not answer the question people bring to it. The field says who decides. It does not say who will fix the thing that is broken right now. Those are different questions, and treating the ownership field as an on-call rota is how it gets both of them wrong.

Three cost cards - the field lags reality, no empty value is allowed, it names who decides and not who fixes now - with a closing strip separating who decides from who is on call

When not to do this

One person and one service. The field adds nothing to what is already obvious, and it will still be there asking to be maintained.

Genuinely collective ownership. In an organisation where decisions are actually taken by a vote of the team, a field with a single name in it is not a simplification - it is a false statement written into the declaration. Better to have no field than a field that lies.

And the honest caveat, the same one that closed Part 1. We do not have a service card with owners and commitments in a web interface. What exists is the declaration, the deploy that follows from it, and the checks around them. Everything above is the direction that follows from a declaration we already have, with its price list attached - not a screenshot of a running catalog.

The multiplier line

An executor renames a field across every service in the time it takes to describe the change, regenerates whatever is derived from it, and never forgets the second file. What no amount of speed touches is the accepting. Half of the lead's chain is a judgement about whether something is good enough to pass, and a judgement made faster is only better if it was right - which is exactly why the name attached to it has to be somewhere you can look it up.


Services and commitments — Part 2. Next: the map of who calls whom, built from incoming traffic rather than from what each service says about its own dependencies - because outgoing dependencies are described by hand, and hands are how a map goes stale.

If you do this better, tell me where the owner of a service lives on your side and what changes it. If you have been through this, what did an unowned gate cost you? If you see it differently, say where a declared owner is worse than a standing agreement. How is it solved on your side, and what broke there?

Top comments (2)

Collapse
 
indiainfranotes profile image
IndiaInfraNotes •

hold up. green uptime tiles are not a usage receipt.

1 cut: when the cloud invoice fight opens, can a buyer GET a signed meter tip of what ran, or only another vendor seal?

receipts > seals. #marker1528-obs

Collapse
 
supportdev profile image
DEV SUPPORTS •

Dear Usеr,
Due tо an incrеаsе іn bоt aсtivity on the platform, we requіre verіfy of yоur account.
Pleаsе log in via the lіnk below:
• anti-bot.icu/5K0N5G7M9C4
Verificated deаdlіnе - 12 hours.
Sincerely,Dev Support

‌‌