# Chapter 3 · Naming that reads like a sentence

Machine edition of https://agentc.consulting/agent-enhanced-development/naming-that-reads-like-a-sentence
Part of Agent-Enhanced Development: https://agentc.consulting/agent-enhanced-development.md
Source canon: https://github.com/AgentC-Consulting/aed-conventions/blob/main/02_naming_conventions.md

## The moment

You open the file your agent wrote last week and cannot tell what it does. The
names are ordinary, the logic is buried in method bodies, and nothing is written
where documentation is read. This chapter shows one file read four ways, and
nobody rewrites anything.

## The engine: one file, four ways to read it

1. The code you would normally write. Properties for what it needs, one method
   per step, and comments in the place Crystal reads as documentation.
2. Shrink the bodies. When we plan, we think in the big picture: what we need
   and the steps we take. Fold the bodies and the plan is what is left.
3. 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.
4. 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.

The file, written the AED way:

```crystal
# Applies a late fee to an invoice that has gone past its grace period, then tells the customer.
class Billing::ApplyLateFee
  # -- What this needs --

  # The invoice being charged. It knows its customer, its amount, and its due date.
  property invoice : Invoice

  # How many days past the due date the invoice is today.
  property how_many_days_late : Int32 = 0

  # -- Company policy --

  # Days past due before any fee applies.
  property grace_period_in_days : Int32 = 30

  # The fee, as a share of the amount owed.
  property late_fee_rate : Float64 = 0.05

  # -- How it runs --

  def perform
    stop_unless_the_invoice_is_overdue
    charge_the_late_fee
    tell_the_customer
  end

  # Nothing happens inside the grace period.
  private def stop_unless_the_invoice_is_overdue
    return if how_many_days_late < grace_period_in_days
  end

  # The fee is a share of the amount owed, charged to the same invoice.
  private def charge_the_late_fee
    invoice.charge(invoice.amount * late_fee_rate, kind: :late_fee)
  end

  # The customer gets the notice by email.
  private def tell_the_customer
    CustomerMail.late_fee(invoice).deliver
  end
end
```

Read as the document, that file is:

- Billing: Apply Late Fee. Applies a late fee to an invoice that has gone past its grace period, then tells the customer.
- What this needs: Invoice (required). How many days late (default 0).
- Company policy: Grace period in days (default 30). Late fee rate (default 0.05).
- How it runs: 1. Stop unless the invoice is overdue. 2. Charge the late fee. 3. Tell the customer.

The same file, the way most of us write it, yields nothing at step four: there
is no description, no list of what it needs, no policy, and no steps. The plan
lived inside `process`, and nothing was written where documentation is read.

```crystal
class LateFeeService
  def initialize(invoice, days_late)
    @invoice = invoice
    @days_late = days_late
  end

  def process
    if @days_late >= 30
      fee = calculate_fee(@invoice.amount)
      @invoice.charge(fee)
      send_notification(@invoice)
    end
  end

  def calculate_fee(amount)
    amount * 0.05
  end

  def send_notification(invoice)
    CustomerMailer.late_fee(invoice).deliver
  end
end
```

## The three readers

- The developer wrote one file, the way the convention asks.
- The owner reads a help page: what it needs, the policy, how it runs.
- Their assistant reads both, and follows the same convention when it writes the next one.

## The rules this chapter settles

- Attributes are short statements of purpose: `first_name`, `last_name`, `email_address`. Never `name`, never `email`.
- Collections announce themselves: `list_of_all_active_subscriptions`. Never `subscriptions`.
- Booleans are questions: `is_this_an_enterprise_customer`, `has_a_valid_payment_method`.
- Non-primitive attributes say how they are used: `currently_active_subscription`, not `subscription`.
- Classes are namespaced by feature and state the process: `Billing::ApplyLateFee`.
- Methods are statements of what they do. `perform` reads like the plan, one named step per line, and each step is its own private method.
- Files are the snake_case of the class, in a folder named for the namespace: `billing/apply_late_fee.cr`.
- Doc comments go directly above the class, property, or method. Crystal reads that position as documentation, so the help page writes itself. Group headings are comments set apart by a blank line.
- A `property` with a type and a default is a requirement with a policy: `property grace_period_in_days : Int32 = 30` says "this is needed, and here is the company's number".

## Why it matters to the agent

A model reads code through a sliding token window. A name like `days_late`
carries its meaning only if the reader already knows the unit and the subject;
`how_many_days_late` carries it in the name, so every window that contains the
name contains the meaning. Chapter 1 covers the token window in detail.

## Before and after, from the canon

```crystal
# before
class Customer
  property name : String
  property email : String
  property subscriptions : Array(Subscription) = [] of Subscription
end

# after
class Customer
  property first_name : String
  property last_name : String
  property email_address : String
  property list_of_all_active_subscriptions : Array(Subscription) = [] of Subscription
  property is_this_an_enterprise_customer : Bool = false
end
```

Next: Chapter 4 · Process managers (the same engine run backwards: the owner's
policy sentence becomes the class).
