agentic examples

API Riffs

API Riffs: before an API ships, sketch it three ways and READ the call sites out loud - the design work happens in the comparing, not the committing. Subject: the journal's group-commit knob (which shipped this round as fsync_every:). Here are the three riffs that could have been, each runnable, each judged at its call site.

Observability & Ops Round 13 Kasper Timm Hansen exit 0

source on github

bundle exec ruby examples/api_riffs.rb

a real captured run

API RIFFS: three shapes for one durability knob

  riff 1 - constructor kwarg:
      journal = ExecutionJournal.new(path:, fsync_every: 20)
    + the trade is visible at construction, greppable in the diff
      that chose it, and IMMUTABLE - nobody weakens durability
      mid-flight three files away.
    - it's a magic integer; 20 of WHAT is one docs-lookup away.

  riff 2 - a named policy object:
      journal = ExecutionJournal.new(path:, durability: Durability.grouped(20))
    + Durability.strict reads as a SENTENCE; new policies (time-
      based flushing) get names without new kwargs; the docs live
      on the object.
    - a whole constant surface for one integer today - the wardrobe
      is bigger than the costume. YAGNI has a case here.

  riff 3 - per-call override:
      journal.record(event, payload, durable: false)
    + maximal flexibility: hot loops opt out, milestones opt in.
    - and that's the indictment: durability becomes a per-CALL-SITE
      opinion. the invariant 'this journal survives crashes' stops
      being a property of the OBJECT and starts being a property of
      every author's judgment forever. flexibility is where
      invariants go to die.

  the riff verdict: shape 1 shipped, and the reading explains why -
  a durability contract belongs to the OBJECT (riff 3 dissolves
  it), and one integer doesn't yet earn a policy wardrobe (riff 2
  can arrive later, wrapping the kwarg, if flush-after-100ms ever
  becomes real). but note what the exercise cost: forty lines and
  ten minutes, versus the years a shipped API lives. riff BEFORE
  you commit - call sites read differently than class definitions,
  and the call site is where your users actually live.

source

# frozen_string_literal: true

# API Riffs: before an API ships, sketch it three ways and READ the
# call sites out loud - the design work happens in the comparing, not
# the committing. Subject: the journal's group-commit knob (which
# shipped this round as fsync_every:). Here are the three riffs that
# could have been, each runnable, each judged at its call site.
#
#   bundle exec ruby examples/api_riffs.rb
#
# Runs offline; every riff executes against the real journal.

require class="s">"bundler/setup"
require class="s">"agentic"
require class="s">"tmpdir"

Agentic.logger.level = class="y">:fatal

def fresh_path(name) = File.join(Dir.tmpdir, class="s">"agentic_riff_#{name}.jsonl").tap { |p| File.delete(p) if File.exist?(p) }

def write_events(journal, n = 5)
  n.times { |i| journal.record(class="y">:task_succeeded, task_id: class="s">"t#{i}", description: class="s">"t#{i}", duration: 0.01, output: nil) }
end

puts class="s">"API RIFFS: three shapes for one durability knob"
puts

# --- riff 1: the constructor kwarg (what shipped) --------------------------------
puts class="s">"  riff 1 - constructor kwarg:"
puts class="s">"      journal = ExecutionJournal.new(path:, fsync_every: 20)"
journal = Agentic:class="y">:ExecutionJournal.new(path: fresh_path(1), fsync_every: 20)
write_events(journal)
journal.sync
puts class="s">"    + the trade is visible at construction, greppable in the diff"
puts class="s">"      that chose it, and IMMUTABLE - nobody weakens durability"
puts class="s">"      mid-flight three files away."
puts class="s">"    - it's a magic integer; 20 of WHAT is one docs-lookup away."
puts

# --- riff 2: the policy object ----------------------------------------------------
puts class="s">"  riff 2 - a named policy object:"
puts class="s">"      journal = ExecutionJournal.new(path:, durability: Durability.grouped(20))"
module Durability
  Every = Struct.new(class="y">:n) do
    def to_fsync_every = n
  end

  def self.grouped(n) = Every.new(n)

  def self.strict = Every.new(1)
end
journal = Agentic:class="y">:ExecutionJournal.new(path: fresh_path(2), fsync_every: Durability.grouped(20).to_fsync_every)
write_events(journal)
journal.sync
puts class="s">"    + Durability.strict reads as a SENTENCE; new policies (time-"
puts class="s">"      based flushing) get names without new kwargs; the docs live"
puts class="s">"      on the object."
puts class="s">"    - a whole constant surface for one integer today - the wardrobe"
puts class="s">"      is bigger than the costume. YAGNI has a case here."
puts

# --- riff 3: the per-call escape hatch --------------------------------------------
puts class="s">"  riff 3 - per-call override:"
puts class="s">"      journal.record(event, payload, durable: false)"
class LeakyJournal < Agentic:class="y">:ExecutionJournal
  def record(event, payload = {}, durable: true, **rest)
    super(event, payload.merge(rest)) # (sketch: durable: false would skip the fsync)
  end
end
journal = LeakyJournal.new(path: fresh_path(3))
write_events(journal)
puts class="s">"    + maximal flexibility: hot loops opt out, milestones opt in."
puts class="s">"    - and that's the indictment: durability becomes a per-CALL-SITE"
puts class="s">"      opinion. the invariant 'this journal survives crashes' stops"
puts class="s">"      being a property of the OBJECT and starts being a property of"
puts class="s">"      every author's judgment forever. flexibility is where"
puts class="s">"      invariants go to die."
puts

puts class="s">"  the riff verdict: shape 1 shipped, and the reading explains why -"
puts class="s">"  a durability contract belongs to the OBJECT (riff 3 dissolves"
puts class="s">"  it), and one integer doesn't yet earn a policy wardrobe (riff 2"
puts class="s">"  can arrive later, wrapping the kwarg, if flush-after-100ms ever"
puts class="s">"  becomes real). but note what the exercise cost: forty lines and"
puts class="s">"  ten minutes, versus the years a shipped API lives. riff BEFORE"
puts class="s">"  you commit - call sites read differently than class definitions,"
puts class="s">"  and the call site is where your users actually live."