Friday’s PR
Tuesday morning of story week 6. I open my laptop. Devon’s PR is at the top of my review queue. He opened it Friday afternoon with one line: “advanced filter, take 4. no POST. yes query string. yes branch_north not north. yes I read RFC 3986.”
The URL is in the OpenAPI diff: https://api.greenfield.lib/v1/books?q=mystery&branch=branch_north&shelf=fiction&on_shelf=true. It’s 9:30. Devon is in standup. He won’t be back for forty minutes. Today you start your own URL, the one Devon flagged the week we first talked about REST. You can’t write a URL until you can read one. So we read his left to right.
Today you will leave with
- How to read a URL left to right and name the five parts.
- What each part commits to and who reads it downstream.
- One URL you wrote yourself.
Read it left to right
The URL in Devon’s diff is https://api.greenfield.lib/v1/books?q=mystery. Left to right, it has five parts, and each one tells some piece of the stack what to do.
scheme
. https. This tells the client which protocol to speak. HTTPS means the connection is encrypted; the rest of the URL is unreadable to anything in the middle. Greenfield is HTTPS, like every public API a 2026 reader is calling.
host
. api.greenfield.lib. This is what DNS resolves to an IP address. The api. is a subdomain Greenfield uses to separate its API server from its public website at greenfield.lib. Two different machines, same registered domain. The subdomain is a Greenfield convention; some APIs use the bare domain and route by path, some put the API on a different domain entirely (api.github.com).
Version. /v1. Devon shipped Greenfield with a version segment from day one because he didn’t want his first design to trap him. When Greenfield ships v2, /v1 keeps working until Devon turns it off. Old clients keep working; new clients opt in. Some APIs hide the version in a header (Accept: application/vnd.greenfield.v2+json) instead. Devon doesn’t, because the header form hides the version from URL readers (logs, bookmarks, the apprentice reviewing the PR).
Base path. Greenfield doesn’t have one. The path starts at the version. Some APIs prefix every endpoint with /api or /platform/v1 to keep the API routable behind a load balancer that also serves the marketing site. Greenfield uses a dedicated subdomain for that job, so no extra prefix.
resource
. /books. This is the noun. The handler that serves this URL is the books handler; its job is to know about books. The query string customizes what books come back, but the resource decides what the response is about. M2L1’s argument was that the URL names a thing. This is the thing.
query string
. ?q=mystery. The ? opens the query string. After it, name=value pairs separated by &. q is the existing filter; mystery is its value. The query string parameterizes the handler. It doesn’t change which handler runs. Same handler, different filter.
sequenceDiagram participant Atlas participant DNS participant API as Greenfield API participant Handler as books handler Atlas->>DNS: resolve api.greenfield.lib DNS-->>Atlas: IP address Atlas->>API: GET /v1/books?q=mystery over HTTPS Note over API: /v1 routes to the v1 server tier API->>Handler: dispatch on /books Note over Handler: q=mystery filters the response Handler-->>Atlas: 200 with matching books
Look at the diagram. Each layer reads only the part it needs.
Now you write
Greenfield’s advanced filter ships this week. The requirements are short:
- Filter by branch (a branch identifier like
branch_north). - Filter by shelf (Greenfield has fiction, biography, reference, periodicals).
- Filter by whether the book is on shelf right now or out on loan.
Write the URL.
Read the parts you just named, in order. Scheme: same. Host: same. Version: same. Resource: same. Three new filters means three new query parameters. Add them to the existing query string.
Here is what the parts give you:
https://api.greenfield.lib/v1/books?q=mystery&branch=branch_north&shelf=fiction&on_shelf=true
Three new query parameters: branch, shelf, on_shelf. Each one a name=value pair, separated from its neighbor by &. Below is the URL with the new parts highlighted. Hover or tap each part to read what it commits to.
A few things you might have done differently and what the parts say about each:
Did you write /v1/books/filter? That makes the URL name an action (filter), not the thing being filtered (books). The block 3 rule was: the resource is the noun. Filter belongs in the query string. /v1/books is still the resource; the query string says how to look at it.
Did you write /v1/books/branch_north/fiction/true? Path segments are for resources and their children, not for filter values. Two reasons it goes badly: the order of segments has to be memorized (which one is the branch, which one is the shelf?), and adding a fourth filter next year means breaking every URL anyone has saved. Query string parameters are named and unordered. Adding ?author=connelly next year is invisible to every existing caller.
Did you write ?filters=branch:branch_north,shelf:fiction,on_shelf:true? That packs the filters into a single query parameter as a string. Greenfield’s server would now have to parse that string itself, defining its own grammar (what does the comma mean, what does the colon mean, how do you escape values that contain commas). The URL standard already defines how & and = work; reusing them is free.
Devon shipped the same URL you just wrote. The PR comment you leave him: nothing. The URL design is right. The doc page that explains what each filter does, what on_shelf=false means versus omitting on_shelf entirely, what happens when branch is misspelled: that is Module 4. Today you wrote the URL.
Words you can drop in standups now
scheme
. Use this when you mean “the https:// part of the URL.” More specific than “protocol”, less ambiguous to engineers.
host . Use this when you mean “the part that DNS resolves to an IP.” Catches all of subdomain, registered domain, and TLD without needing to name each one.
resource . Use this when you mean “the noun in the path that names what the response will be about.” The opposite of an action.
query string
. Use this when you mean “the part after the ? that customizes the response without changing which handler runs.”
AI co pilot tip
Tool: Cursor (the code editor with AI built in).
The situation. You’re reviewing a PR that adds a new endpoint, or you’re drafting one yourself. The diff has a URL pattern in it (the OpenAPI YAML, the routes file, a comment block). You want a second opinion on whether the URL design follows the parts.
The prompt (highlight the URL or the OpenAPI path entry, open the Cursor chat panel):
Here is a URL pattern: <paste>. Read it left to right and name the five parts in left-to-right order. For each part, say one sentence on whether the choice fits common REST conventions. If the URL names an action instead of a resource, say so. If a filter is in the path that should be in the query string, say so. Do not hedge.
What to expect back. Five short lines, one per part, and a verdict in one sentence on the URL as a whole. If Cursor finds a verb in the path, the verdict should say so.
What to watch for. Cursor sometimes tries to be diplomatic about a URL that names an action (“this is a stylistic choice”). Push back: “is this URL RPC or REST?” The second pass usually picks one. Cursor is at its best when it has the surrounding code as context, so ask the question with the routes file or the OpenAPI YAML open in the editor.
Before you go
Look at the URL bar of any web page you have open. Read it left to right. Name the parts that are there and the parts that aren’t.
Next week at Greenfield
Next week the URL gets harder. The parts we skipped today (fragments, port, path parameters) and the parts that hurt when they go wrong (encoding, traversal, ambiguous slashes) all show up. Devon has opinions about those too.
Maya.