Agent-Enhanced Development · Chapter 3

Naming that reads like a sentence

You open the file your agent wrote last week and can't tell what it does. Here is one file, read four ways, and nobody rewrites anything. Scroll, or press a step.

The code you would normally write. Properties for what it needs, one method per step, and comments in the place Crystal reads as documentation.

Shrink the bodies. When we plan, we think in the big picture: what we need, and the steps we take. The bodies are detail. Fold them and the plan is what is left.

Hide the syntax and read it. Take away def, end, colons, and underscores. What remains is a list of what it needs and a list of what it does, in plain words.

It becomes the document. The comments were written where Crystal reads them as documentation, so the same file is the help page: what this needs, the policy, how it runs.

billing · agentc
1# Applies a late fee to an invoice that has gone past its grace period, then tells the customer.
2class Billing:::ApplyLateFee
3# -- What this needs --
4
5 # The invoice being charged. It knows its customer, its amount, and its due date.
6 property invoice : · Invoice — required
7
8 # How many days past the due date the invoice is today.
9 property how_many_days_late : · Int32 = , starts at — default 0
10
11# -- Company policy --
12
13 # Days past due before any fee applies.
14 property grace_period_in_days : · Int32 = , starts at — default 30
15
16 # The fee, as a share of the amount owed.
17 property late_fee_rate : · Float64 = , starts at — default 0.05
18
19# -- How it runs --
20
21 def perform
22 stop_unless_the_invoice_is_overdue
23 charge_the_late_fee
24 tell_the_customer
25 end
26
27 # Nothing happens inside the grace period.
281. private def stop_unless_the_invoice_is_overdue
29 return if how_many_days_late < grace_period_in_days
30 end
31
32 # The fee is a share of the amount owed, charged to the same invoice.
332. private def charge_the_late_fee
34 invoice.charge(invoice.amount * late_fee_rate, kind: :late_fee)
35 end
36
37 # The customer gets the notice by email.
383. private def tell_the_customer
39 CustomerMail.late_fee(invoice).deliver
40 end
41end
The developerwrote one file, the way the convention asks.
The ownerreads a help page: what it needs, the policy, how it runs.
Their assistantreads both, and follows the same convention when it writes the next one.

Now switch to the second tab and take it through the same four steps. The plan lived inside a method body, so folding erased it, and there was nothing written where documentation is read.

← The bookReach the document and the next chapter opens.

What this chapter settles

Naming conventions carry the meaning, so every window that holds the name holds the meaning.

These are the AED naming conventions for AI coding agents: the rules your agent applies when it writes or renames a file for you, and the rules a teammate's agent can read back.

  • Attributes are short statements of purpose.Which name? Whose email? The name answers before anyone has to ask.
    namefirst_name
    emailemail_address
  • Collections announce themselves.Active only, and a list: two facts the old name kept to itself.
    subscriptionslist_of_all_active_subscriptions
  • Booleans are questions.A yes-or-no name reads as the condition it guards.
    enterpriseis_this_an_enterprise_customer
    payment_method_presenthas_a_valid_payment_method
  • Classes are namespaced by feature and state the process.The name is the sentence the owner would say.
    LateFeeServiceBilling::ApplyLateFee
  • Methods say what they do, and perform reads like the plan.One named step per line, each step its own private method.
    processperform
    calculate_feecharge_the_late_fee
  • A property with a type and a default is a requirement with a policy.Required means no default. The default is the company's number.
    property grace_period_in_days : Int32 = 30
  • Doc comments go where Crystal reads them.Directly above the class, property, or method. Group headings are comments set apart by a blank line. That is why step four exists.
    # Days past due before any fee applies.
    property grace_period_in_days : Int32 = 30
  • Files match the class.Snake case of the class, in a folder named for the namespace.
    billing/apply_late_fee.cr

From the canon

The customer class, before and after. Drag the divider.

customer.cr · before and after
customer.cr — before
1class Customer
2 property name : String
3 property email : String
4 property subscriptions : Array(Subscription) = [] of Subscription
5
6 def initialize(@name, @email)
7 end
8end
customer.cr — after
1class Customer
2 property first_name : String
3 property last_name : String
4 property email_address : String
5 property list_of_all_active_subscriptions : Array(Subscription) = [] of Subscription
6 property is_this_an_enterprise_customer : Bool = false
7
8 def initialize(@first_name, @last_name, @email_address, @is_this_an_enterprise_customer = false)
9 end
10end

The name before could have been a first name, a full name, or a company. subscriptions did not say it held only the active ones. The after file says both, and says whether this is an enterprise customer, which the sales team needed and the agent had no way to know.

A window of tokens, sliding

Why it matters to the agent

A model reads through a sliding window. Put the meaning inside the name.

days_late means something only if the reader already knows the unit and the subject. how_many_days_late carries both, so every window that holds the name holds the meaning.

Reading with an agent? Hand it the machine edition of this chapter. The source is chapter 02 of the canon.