Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Tropical On Rails 2026 — Generators are APIs: D...

Tropical On Rails 2026 — Generators are APIs: Designing Better DX in Rails

Slides reg my talk at Tropical On Rails 2026 in Sao Paulo, Brazil.

Avatar for Rafael Peña-Azar

Rafael Peña-Azar

April 10, 2026

More Decks by Rafael Peña-Azar

Other Decks in Programming

Transcript

  1. $ rails g talk generators_are_apis \ --at=tropical_on_rails \ --speaker=rpaweb Running

    via spring preloader in process 12345 invoke rails insert event: Tropical-On-Rails insert schedule: 2026-04-10 insert location: São-Paulo status confirmed create generators_are_apis.live create talks/tor26/slides.html.erb Talk successfully generated. Ready to run at Tropical On Rails.
  2. Generators are APIs Designing Better DX in Rails Rafael Peña-Azar

    · Tropical On Rails 2026 @rpaweb rpaweb.github.io
  3. Generator rails g model User \ name:string \ email:string \

    --timestamps API call POST /api/users body: { name: string, email: string } Tropical On Rails 2026
  4. ❏ Act I — We’ve been thinking about this wrong.

    ❏ Act II — What bad DX looks like. ❏ Act III — What good DX looks like. ❏ Act IV — The bigger picture. We’re gonna go through 4 acts. Tropical On Rails 2026
  5. ❏ Act I — We’ve been thinking about this wrong.

    ❏ Act II — What bad DX looks like. ❏ Act III — What good DX looks like. ❏ Act IV — The bigger picture. We’re gonna go through 4 acts. Tropical On Rails 2026
  6. HOW WE TREAT THEM Input a name → get some

    files → move on WHAT THEY ACTUALLY ARE The first point of contact between a developer and your architecture Tropical On Rails 2026
  7. What we expect from a good API Clear Predictable Consistent

    Useful ✅ ✅ ✅ ✅ Confusing Unpredictable Unrelated Useless ❌ ❌ ❌ ❌ Naming Inputs Outputs Errors Tropical On Rails 2026
  8. API concept Generator equivalent Endpoint name Generator name Request params

    Arguments & flags Response body Generated files Error messages Failure output Same interface. Different medium. Tropical On Rails 2026
  9. ❏ Act I — We’ve been thinking about this wrong.

    ❏ Act II — What bad DX looks like. ❏ Act III — What good DX looks like. ❏ Act IV — The bigger picture. Acts we’re following on this talk. Tropical On Rails 2026
  10. Problem 1: Ambiguity $ rails g jet_ui:eject Button $ rails

    g jet_ui:eject button $ rails g jet_ui:eject btn Tropical On Rails 2026
  11. Problem 3: No options. No control. $ rails g jet_ui:eject

    btn create app/components/jet_ui/btn_component.rb create app/assets/stylesheets/jet_ui/btn.css create test/components/jet_ui/btn_component_test.rb create test/components/previews/jet_ui/btn_component_preview.rb # No --no-test. No --no-preview. No flags at all. # The generator decided for you. Tropical On Rails 2026
  12. You just described a BAD API. You just didn’t call

    it that way. Tropical On Rails 2026
  13. ❏ Act I — We’ve been thinking about this wrong.

    ❏ Act II — What bad DX looks like. ❏ Act III — What good DX looks like. ❏ Act IV — The bigger picture. Acts we’re following on this talk. Tropical On Rails 2026
  14. rails g jet_ui:eject Button rails g jet_ui:eject button rails g

    jet_ui:eject btn rails g jet_ui:eject btn card # Documented convention: # use the component short name. The name should tell you exactly what you'll get. Before. After. Principle 1. Naming is a contract. Tropical On Rails 2026
  15. Principle 2. Predictable inputs. class JetUi::EjectGenerator < Rails::Generators::Base desc "Ejects

    JetUi components into your app for local customisation." argument :components, type: :array, banner: “component [component …]”, desc: “Component(s) to eject (e.g. btn card)” def eject_components components.map(&:downcase).each do |name| MANIFEST[name][:files].each do |entry| template entry[:src], entry[:dest] end end end end Tropical On Rails 2026
  16. Principle 3. Tell people what you did and why. class

    JetUi::EjectGenerator < Rails::Generators::Base # rest of the generator logic here def eject_components components.each do |component| say "Ejecting #{component}..." # logic for generating files say " #{component} ejected.", :green end end def print_next_steps say "Done! Ejected: #{components.join(', ')}", :green say "Run your tests: bundle exec rake test", :cyan end end
  17. Principle 3. Tell people what you did and why. $

    rails g jet_ui:eject btn Ejecting btn... create app/components/jet_ui/btn_component.rb create app/assets/stylesheets/jet_ui/btn.css btn ejected. Done! Ejected: btn Run your tests: bundle exec rake test
  18. $ rails g jet_ui:eject buton create app/components/jet_ui/ Buton_component.rb # Silently

    creates files for # a component that does not exist. $ rails g jet_ui:eject buton error Unknown component: buton Available: btn, card, modal, alert, badge Did you mean? btn Example: rails g jet_ui:eject btn card Before. After. Principle 4. Errors should tell how to fix them.
  19. Principle 5. Design for extension, not rewriting. class JetUi::EjectWithStorybookGenerator <

    JetUi::EjectGenerator desc "Like jet_ui:eject, but also generates Storybook stories." def generate_stories components.each do |component| template "story.js.tt", "stories/jet_ui/#{component}_component.stories.js" end end end Tropical On Rails 2026
  20. ❏ Act I — We’ve been thinking about this wrong.

    ❏ Act II — What bad DX looks like. ❏ Act III — What good DX looks like. ❏ Act IV — The bigger picture. Acts we’re following on this talk. Tropical On Rails 2026
  21. $ rails g jet_ui:eject btn ▼ dev 1 same structure

    dev 2 same structure dev 3 same structure dev 4 same structure Tropical On Rails 2026
  22. "The best generators are the ones you never have to

    explain. The code they produce speaks for itself — because you designed the conversation." Tropical On Rails 2026
  23. $ rails g talk generators_are_apis \ --at=tropical_on_rails \ --speaker=rpaweb Running

    via spring preloader in process 12345 invoke rails insert event: Tropical-On-Rails insert schedule: 2026-04-10 insert location: São-Paulo status confirmed create generators_are_apis.live create talks/tor26/slides.html.erb Talk successfully generated. Ready to run at Tropical On Rails.
  24. ❏ Act I — We’ve been thinking about this wrong.

    ❏ Act II — What bad DX looks like. ❏ Act III — What good DX looks like. ❏ Act IV — The bigger picture. We covered all acts! Tropical On Rails 2026
  25. $ pragon create my_new_rails_app \ --framework=rails $ pragon create my_new_php_app

    \ --framework=laravel $ pragon create my_new_phx_app \ --framework=phoenix $ pragon add stripe $ pragon remove search $ pragon run:migrations $ pragon run:deployment CORE FATE