Ownership as code
In software development, ownership is a term that’s often thrown around but poorly understood. What does it mean to own something? Why is this something we strive to attain?
At Samsung Ads, the engineering team had been growing very rapidly (2x yearly) over the last 3 years and it’s become clear that ownership is something critical. Who owns that codebase? Who’s responsible for the uptime of that service? Who should I reach out to if the service goes down? When you’re a startup these questions are trivial, but as you grow things becomes messy (shared monolith, old codebases, etc).
What is code ownership? #
Before we continue, I think it’s important to define what is ownership. Code ownership can be broken down into 3 facets:
Responsibility #
If you own a codebase, you should be responsible for it. You should care that it’s well maintained and make sure that it gets the right level of attention (new features, code cleanups, updating dependencies, architecture improvements, etc).
Accountability #
If you own a codebase (service), you should also be accountable if you introduce a bug or if it breaks in production. You should be pro-active in fixing it and communicating (incident management) to anyone that depends on it.
Autonomy #
If you own a codebase, you should be setting the coding standards and enforcing them (CI checks and PR reviews). You should be in charge of the architecture and you should document (ADRs). You should be in control of the service deployments in production (CD). You also should be in control of the app/product metrics and monitoring for that service.
Awkward phase #
Now that I’ve explained what ownership is, let’s get back to the story. Clearly, we’re not the first one to experience the pains associated with hyper-growth. There’s plenty of organizational models out there that can solve these issues. Most famously, the service oriented architecture (SOA) which was popularized by Amazon (see Jeff Bezos 2002 memo). But how do you go from the “startup organizational model” (aka no model) to a full out SOA model? What structure or tools can you use to untangle the mess during this transition phase?
The first thing we tried was to build a Google sheet that documents all the products, features and services and assign them clear owners. After a few weeks of back and forth it became clear that this wasn’t the right tool for the job. The Google sheet was complex to navigate and only a few individuals had enough product depth to keep it up to date.
Ownership as code #
As a software developer (I’m still a software dev in my heart), my go to tool for solving problems is code. In this case, it took me a while to realize that code could actually help me since this is an organizational issue. My first realization was that most of the data in our Google sheet was denormalized (remember that database class?). The second one, was that each owner (management, product, engineering) should be in control of updating the model at the level they understand. And voilà, the best way to solve this problem was to introduce a data model that can represent our organizational structure. And so is born the concept of “ownership as code”.
I decided to abuse some of the tools I love as a developer (mostly Git and Github) to build something simple that answers our current needs. Since this data doesn’t change often, I decided to build a file based database (yaml-file-db) that uses YAML documents as source data and JSON schemas for validation. The database is committed into Git which gave us history for free. The database is hosted on Github, which give us user management for free and also pull requests to review changes. The big advantage of having this as code is that I can actually automate and enforce code ownership using Github Actions and the Github API. Every time the “ownership database” is updated, we do the following:
- Synchronise Github teams
- Generate CODEOWNERS file for each repository (via a PR)
- Check for “orphan” codebases
- Check for codebases with multiple owners
- Generate metrics (percentage of codebase with a CODEOWNERS file, percentage of codebase with multiple owners, etc)
By doing this we make sure that our model is always in sync with reality. We also get metrics (for free) to track progress which is a nice bonus.
Database example #
The directory structure matches the database structure. Each folder within the database directory is a table. Each YAML document within a table directory is a row. The filename of the YAML document is used as the primary key for that row.
database/
features/
comment.yml
post.yml
services/
comment-api.yml
neo4j.yml
post-api.yml
postegresql.yml
teams/
alpha.yml
beta.yml
Keys that match a table name (singular or plural form) are automatically converted into relationships.
# features/comment.yml
description: Create and edit comments
services:
- comment-api
- neo4j
# services/neo4j.yml
description: Graph database
team: alpha
# teams/alpha.yml
name: Alpha team
product_owner: Alice
engineers:
- name: John Doe
github: johndoe
- name: Jane Doe
github: janedo
You can easily navigate the data using the YDB:Database
object. Each table is indexed by primary key and columns are exposed via accessors.
irb(main):001:0> require 'yaml-file-db'
=> true
irb(main):002:0> db = YDB::Database.new("/path/to/database", "/path/to/schemas").build
=> #<YDB::Database:0x00007fedb1922c68 ... >
irb(main):03:0> db.features["comment"].services.first.id
=> "comment-api"
irb(main):04:0> db.features["comment"].services.first.team.engineers
=> [{"name"=>"John Doe", "github"=>"johndoe"}, {"name"=>"Jane Doe", "github"=>"janedoe"}]
Next steps #
Code ownership is just one piece of the puzzle and we still have a lot of work to do to get to “full ownership”. In my next post, I’ll cover our progress on the “Ownership as code” journey and talk about other important aspects of ownership.