The footprint of one feature
Owning a feature end to end starts with knowing how big a feature is. Not in lines: in places you have to touch, and places that can break without you noticing.
Counting Partners
The partners feature in this application. Sponsor logos on the roadmap, hiring cards, an inquiry form, an admin CRUD, and, since this course was first written, a partner API, signed webhooks, a sign-in handoff and a learner consent screen. Here is everything it consists of.
$ find app -path '*partners*' -type f | wc -l
70
| Layer | Count | Examples |
|---|---|---|
| Models | 8 | Partner, Role, Inquiry, CourseSponsorship, ApiToken, Referral, HandoffNonce, partners.rb |
| Controllers | 9 | Three admin (partners, graduates, inquiries), inquiries, consents, and four in the partner API |
| Queries | 11 | ActivePartnersQuery, TrackSponsorsQuery, HiringQuery, LearnersQuery, seven more |
| Services | 9 | SavePartnerService, FetchSiteIconService, SubmitInquiryService, RedeemHandoffTokenService, WebhookTransport, four more |
| Jobs | 4 | FetchSiteIconJob, DeliverWebhookJob, AnnounceCertificateEarnedJob, PruneHandoffNoncesJob |
| Mailers | 1 | InquiryMailer, with an HTML and a text template |
| Policies | 9 | Partners::Tiers, WebhookSignature, WebhookCircuit, ApiScopes, five more |
| Views | 19 | Admin forms, public partials, mailer templates, three API jbuilder views |
| Spec files | 27 | Request, service, job, policy and query specs, plus factories |
| Migrations | 9 | From create_partners to the API token and referral tables |
| Routes | 6 entries | An admin namespace, the API namespace, the handoff, the public page, the inquiry POST, and a redirect |
Around a hundred files: seventy in app/, twenty-seven in spec/, nine migrations. And that count deliberately excludes the things that are not files. The locale keys in two languages, the Active Storage attachment, the queues the jobs run on, and whatever the roadmap page does with TrackSponsorsQuery.
flowchart TB
F["one feature: Partners"] --> M["8 models"]
F --> C["9 controllers, 4 of them an API"]
F --> Q["11 queries"]
F --> S["9 services"]
F --> J["4 jobs"]
F --> ML["1 mailer, 2 templates"]
F --> V["19 views"]
F --> T["27 spec files, 9 migrations, 6 route entries"]
F --> N["and: locale keys, storage, queues, a public page"]
Why the count matters
Because the instinct when someone says "I'll take the partner logo work" is to picture the service object. And the service object is one file out of about a hundred.
Every one of those layers is a place where the feature can be half-finished. A query added and never called. A locale key in one language. A migration that ran in development and not in production. A job that enqueues and silently does nothing. A view partial with a nil guard nobody needed until the day they did.
Ownership means the whole column, not the interesting file in the middle.
Eleven queries
Look at that row again. Eleven query objects for one feature: ActivePartnersQuery, TrackSponsorsQuery, CourseSponsorQuery, HiringQuery, four behind the partner API, and three admin ones.
Two things follow from that number.
The read side is bigger than the write side. Seven of the nine services write; eleven queries read. That is normal and rarely anticipated, and it is why "add a field to partners" is never one file. Every query that returns partners may need to know about it.
Somebody already asked your question. Before adding a twelfth query, the honest first step is reading the eleven. This is the single most common source of duplicated work on a feature with an existing footprint, and it costs ten minutes to avoid.
The layers that get forgotten
Of the eleven rows, four are the ones that get left half-done, and they are the same four every time:
Locale keys. The feature works in the language you developed in.
The mailer's text template. The HTML version gets attention; the plain-text one is rendered by a client you do not use.
The job's failure path. It enqueues, so it looks done.
The migration's production shape. It ran on a development database with forty rows.
Lessons 4 and 5 are about these four, because they are where "I finished the feature" and "the feature works" come apart.
The definition
Owning a feature end to end means: you know every way in, every way it fails, and how you would find out that it stopped working.
Three clauses. Lesson 2 is the first, lessons 3 and 4 are the second, and lesson 5 is the third, which is the one nobody owns by default.
Key takeaways
- The Partners feature is around a hundred files across eleven layers, plus locale keys, storage and queues.
- The service object everyone pictures is one file out of about a hundred.
- Eleven queries read, seven services write: the read side is bigger and rarely anticipated.
- Before adding a query, read the eleven that exist; it is the most common source of duplicated work.
- Owning end to end: you know every way in, every way it fails, and how you would find out it stopped.
Keep going with lesson 2
Every way in
Traces the two paths into the logo fetch service and why their timing, purge and error reporting differ.
A free account also gives you the quizzes, your progress and notes, and the discussion.