{"id":1288,"date":"2026-09-04T09:43:22","date_gmt":"2026-09-04T08:43:22","guid":{"rendered":"https:\/\/ruby-doc.org\/learn\/?p=1288"},"modified":"2026-09-04T15:00:06","modified_gmt":"2026-09-04T14:00:06","slug":"business-process-automation-with-ruby","status":"publish","type":"post","link":"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/","title":{"rendered":"Business Process Automation With Ruby: From Script to Governed Workflow"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Business process automation sounds grand. In practice, the useful examples are usually rather ordinary: copying approved data into another system, raising a purchase request, sending a renewal reminder, or moving a case to the next person. That is exactly why Ruby is a good fit. It gives me enough structure to make the workflow dependable without burying a modest problem under an elaborate platform.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When I approach business process automation with Ruby, I do not begin with the code. I begin by asking <a href=\"https:\/\/www.reddit.com\/r\/ruby\/comments\/1c2e00h\/best_way_to_do_not_slow_metaprogramming_in_ruby_33\/\">what must still be true<\/a> when the network is slow, a worker stops halfway through, or somebody submits the same request twice. The happy path is rarely the difficult part. Trust comes from what the automation does around the edges.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this article, I will sketch a purchase-approval workflow. The example is deliberately small, but the design applies equally well to onboarding, invoice processing, account reviews and other internal operations.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Start by drawing the boundary<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Before I create a class, I write down six things: the trigger, the input, the decision, the action, the exception path and the evidence left behind. If one of those is vague, the process is not ready to automate.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Trigger:<\/strong> a purchase request is submitted.<\/li>\n\n\n\n<li><strong>Input:<\/strong> requester, supplier, amount, currency and business reason.<\/li>\n\n\n\n<li><strong>Decision:<\/strong> which approval route applies?<\/li>\n\n\n\n<li><strong>Action:<\/strong> create an approval task in the destination system.<\/li>\n\n\n\n<li><strong>Exception:<\/strong> hold incomplete or rejected requests for a person to inspect.<\/li>\n\n\n\n<li><strong>Evidence:<\/strong> record who or what made each decision and when.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">I am wary of workflows that mix all six concerns in a controller or one long background job. They may work on Tuesday morning and become impossible to reason about when something fails on Friday evening. I prefer a small policy object for the business decision and separate code for transport, persistence and reporting.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Keep business rules visible<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A routing rule should read like a rule. Here is a deliberately simple version:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class ApprovalPolicy\n  def route_for(amount_cents:, regulated_supplier:)\n    return :compliance_review if regulated_supplier\n    return :director_review if amount_cents &gt;= 500_000\n    return :manager_review if amount_cents &gt;= 100_000\n\n    :automatic_approval\n  end\nend\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The numbers are examples, not universal recommendations. What matters is that the rule is explicit, testable and independent of the HTTP client or queue. A reviewer can understand it without following a trail through callbacks.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I would also validate the request before it reaches this object. Missing currency, a negative amount or an unknown supplier should not become a mysterious queue failure. Reject bad input at the boundary and explain what the user can correct.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Move slow work out of the request<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Calling another system can take seconds, and sometimes it does not return at all. In a Rails application I normally move that work into Active Job. The current <a href=\"https:\/\/guides.rubyonrails.org\/active_job_basics.html\">Rails guide to Active Job<\/a> covers queueing, recurring tasks, concurrency controls, error reporting and retry behaviour. Rails 8 uses the database-backed Solid Queue as its default production backend, although Active Job can sit over other queue adapters.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>class ProcessPurchaseRequestJob &lt; ApplicationJob\n  queue_as :operations\n\n  retry_on Net::OpenTimeout,\n           Net::ReadTimeout,\n           wait: :polynomially_longer,\n           attempts: 5\n\n  discard_on ActiveJob::DeserializationError\n\n  def perform(purchase_request_id)\n    purchase_request = PurchaseRequest.find(purchase_request_id)\n    return if purchase_request.approval_submitted_at?\n\n    response = ApprovalGateway.new.submit(purchase_request)\n\n    purchase_request.with_lock do\n      return if purchase_request.approval_submitted_at?\n\n      purchase_request.update!(\n        external_approval_id: response.fetch(\"id\"),\n        approval_submitted_at: Time.current\n      )\n\n      AuditEvent.create!(\n        subject: purchase_request,\n        action: \"approval_submitted\",\n        details: { external_id: response.fetch(\"id\") }\n      )\n    end\n  end\nend\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Retries need judgement. A timeout is often worth retrying; invalid input usually is not. I set a finite attempt count, allow delays to grow and make terminal failures visible to an operator. Retrying forever is not resilience. It is a way of hiding a broken process.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I also enqueue only after the request has been committed to the database. Otherwise a fast worker may try to load a row that the web transaction has not finished creating.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Assume every job can run twice<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is the rule I would most want a team to remember: background work can be delivered more than once. A worker can complete the remote request and die before saving the response locally. Another worker then sees an unfinished record and tries again.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The local guard in the job helps, but it does not close that gap. If the receiving API supports idempotency keys, I send a stable key derived from the local record:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>require \"json\"\nrequire \"net\/http\"\n\nclass ApprovalGateway\n  ENDPOINT = URI(\"https:\/\/api.example.test\/v1\/approval_requests\")\n\n  def submit(purchase_request)\n    request = Net::HTTP::Post.new(ENDPOINT)\n    request&#91;\"Content-Type\"] = \"application\/json\"\n    request&#91;\"Idempotency-Key\"] =\n      \"purchase-request:#{purchase_request.id}\"\n\n    request.body = JSON.generate(\n      supplier: purchase_request.supplier_name,\n      amount_cents: purchase_request.amount_cents,\n      currency: purchase_request.currency\n    )\n\n    http = Net::HTTP.new(ENDPOINT.host, ENDPOINT.port)\n    http.use_ssl = true\n    http.open_timeout = 2\n    http.read_timeout = 5\n\n    response = http.request(request)\n    raise \"Approval API returned #{response.code}\" unless response.is_a?(Net::HTTPSuccess)\n\n    JSON.parse(response.body)\n  end\nend\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The remote system should return the original result when it sees that key again rather than creating a second approval. If an API offers no such feature, I look for another stable deduplication mechanism. Hope is not a duplicate-prevention strategy.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The standard library is quite capable of this kind of integration. The <a href=\"https:\/\/ruby-doc.org\/3.4\/stdlibs\/net\/http\/Net\/HTTP.html\">Net::HTTP documentation<\/a> is worth reading before adding another dependency simply to make one request. For a larger client, I would add typed errors, response validation, authentication and tests around every status the remote service promises.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Governance belongs in the workflow<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Governance is often treated as paperwork added after an automation has been built. I think it belongs in the model from the beginning. The code should make it possible to answer who initiated an action, which rule ran, what data crossed the system boundary, whether a person approved it and how the decision can be reversed.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Whether the work is internal or supported by a specialist, I would insist on a few practical controls:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Give the automation the least access it needs.<\/li>\n\n\n\n<li>Keep secrets out of source code and logs.<\/li>\n\n\n\n<li>Require human approval for costly, sensitive or unusual cases.<\/li>\n\n\n\n<li>Record rule versions as well as outcomes.<\/li>\n\n\n\n<li>Provide a way to pause the workflow without deploying new code.<\/li>\n\n\n\n<li>Set retention rules for personal and commercially sensitive data.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">A human checkpoint should be a real state in the workflow, not an email sent into the void. I like statuses such as <code>awaiting_manager_review<\/code> and <code>awaiting_compliance_review<\/code> because they can be queried, measured and shown in an interface. That makes unfinished work visible.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Log decisions, not secrets<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A log line saying \u201cjob completed\u201d tells me almost nothing. I want a run identifier, the record involved, the route selected, the duration and the outcome. I do not want access tokens, full request bodies or personal data copied into logs for convenience.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Rails applications can use Active Support instrumentation to publish and subscribe to workflow events:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ActiveSupport::Notifications.instrument(\n  \"approval_request.automation\",\n  purchase_request_id: purchase_request.id,\n  route: route\n) do\n  ApprovalGateway.new.submit(purchase_request)\nend\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Metrics should answer operational questions. How many requests are waiting? How old is the oldest one? Which route fails most often? What percentage still needs manual correction? A dashboard full of queue internals can be technically impressive while saying very little about whether the business process works.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Test the rule and the awkward cases<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I start with the policy because it is cheap to test and expensive to misunderstand:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>require \"test_helper\"\n\nclass ApprovalPolicyTest &lt; ActiveSupport::TestCase\n  test \"routes a regulated supplier to compliance\" do\n    route = ApprovalPolicy.new.route_for(\n      amount_cents: 25_000,\n      regulated_supplier: true\n    )\n\n    assert_equal :compliance_review, route\n  end\n\n  test \"routes a large request to a director\" do\n    route = ApprovalPolicy.new.route_for(\n      amount_cents: 600_000,\n      regulated_supplier: false\n    )\n\n    assert_equal :director_review, route\n  end\nend\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then I test the less comfortable scenarios: the same job runs twice, the API times out after accepting the request, the response omits an expected field, the local record is deleted, and an operator pauses the process. Those tests describe the real contract of the automation far better than another demonstration of the happy path.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If any syntax in these examples is unfamiliar, the <a href=\"https:\/\/ruby-doc.org\/learn\/ruby-code-examples\/\">Ruby code examples<\/a> guide provides a broader tour of methods, classes, collections and control flow.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Know when the script has become a system<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A short Ruby script is often the right beginning. It lets a team prove that a repetitive task can be represented accurately. I would resist turning that first success into a permanent unattended process without adding ownership, persistence, retries, monitoring and an exception queue.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There is a useful dividing line here. A script helps one person complete a task. A system keeps working when that person is away, the input is imperfect and a dependency is unavailable. Crossing that line deserves deliberate engineering.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/dev.to\/kopylov_vlad\/why-i-love-ruby-44g9\">Ruby remains a strong language<\/a> for this work because readable objects make business rules approachable, its standard library handles common integration jobs, and Rails supplies durable application patterns when the workflow grows. The deciding factor, though, is not the language. It is whether the automation leaves the process easier to understand and safer to operate.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">My checklist before switching it on<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Can I state the trigger and the owner in one sentence?<\/li>\n\n\n\n<li>Are invalid inputs rejected with a useful explanation?<\/li>\n\n\n\n<li>Is the decision logic visible and covered by tests?<\/li>\n\n\n\n<li>Can every external action be retried without duplication?<\/li>\n\n\n\n<li>Are timeouts and retry limits explicit?<\/li>\n\n\n\n<li>Does sensitive work stop for human approval?<\/li>\n\n\n\n<li>Can an operator see failures and resume them safely?<\/li>\n\n\n\n<li>Does the audit trail explain what happened without leaking secrets?<\/li>\n\n\n\n<li>Can the workflow be paused quickly?<\/li>\n\n\n\n<li>Do we know what success costs and how it will be measured?<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">That checklist is intentionally less exciting than the automation demo. It is also the part that makes the demo useful six months later. My aim with business process automation in Ruby is not to remove people at any cost. It is to let software handle repeatable work while keeping judgement, accountability and recovery where they belong.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>James Britt explains how he approaches business process automation with Ruby, from visible rules and background jobs to idempotency, audit trails and human approval.<\/p>\n","protected":false},"author":3,"featured_media":1287,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-1288","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-programming"],"blocksy_meta":{"styles_descriptor":{"styles":{"desktop":"","tablet":"","mobile":""},"google_fonts":[],"version":7}},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Business Process Automation With Ruby: A Practical Guide<\/title>\n<meta name=\"description\" content=\"James Britt explains how to build business process automation with Ruby using background jobs, idempotency, audit logs and human approval.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Business Process Automation With Ruby: A Practical Guide\" \/>\n<meta property=\"og:description\" content=\"James Britt explains how to build business process automation with Ruby using background jobs, idempotency, audit logs and human approval.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/\" \/>\n<meta property=\"og:site_name\" content=\"Ruby-Doc Learn\" \/>\n<meta property=\"article:published_time\" content=\"2026-09-04T08:43:22+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-09-04T14:00:06+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/ruby-business-process-automation.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1659\" \/>\n\t<meta property=\"og:image:height\" content=\"948\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"James Britt\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"James Britt\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/business-process-automation-with-ruby\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/business-process-automation-with-ruby\\\/\"},\"author\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/james-britt\\\/#james-britt\"},\"headline\":\"Business Process Automation With Ruby: From Script to Governed Workflow\",\"datePublished\":\"2026-09-04T08:43:22+00:00\",\"dateModified\":\"2026-09-04T14:00:06+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/business-process-automation-with-ruby\\\/\"},\"wordCount\":1394,\"publisher\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/business-process-automation-with-ruby\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/ruby-business-process-automation.png\",\"articleSection\":[\"Programming\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/business-process-automation-with-ruby\\\/\",\"url\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/business-process-automation-with-ruby\\\/\",\"name\":\"Business Process Automation With Ruby: A Practical Guide\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/business-process-automation-with-ruby\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/business-process-automation-with-ruby\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/ruby-business-process-automation.png\",\"datePublished\":\"2026-09-04T08:43:22+00:00\",\"dateModified\":\"2026-09-04T14:00:06+00:00\",\"description\":\"James Britt explains how to build business process automation with Ruby using background jobs, idempotency, audit logs and human approval.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/business-process-automation-with-ruby\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/business-process-automation-with-ruby\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/business-process-automation-with-ruby\\\/#primaryimage\",\"url\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/ruby-business-process-automation.png\",\"contentUrl\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/ruby-business-process-automation.png\",\"width\":1659,\"height\":948,\"caption\":\"A dependable Ruby automation needs controlled execution, human approval and a usable audit trail.\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/business-process-automation-with-ruby\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Business Process Automation With Ruby: From Script to Governed Workflow\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/#website\",\"url\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/\",\"name\":\"Ruby-Doc Learn\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/#organization\",\"name\":\"Ruby-Doc Learn\",\"url\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/ruby-doc-logo-transparent.png\",\"contentUrl\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/ruby-doc-logo-transparent.png\",\"width\":1650,\"height\":305,\"caption\":\"Ruby-Doc Learn\"},\"image\":{\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/james-britt\\\/#james-britt\",\"name\":\"James Britt\",\"image\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/wp-content\\\/uploads\\\/2026\\\/09\\\/jgb_self-portrait-20140914.png\",\"description\":\"Creator and long-term maintainer of Ruby-Doc.org, founder of Neurogami, and author of practical Ruby tutorials for Ruby-Doc Learn.\",\"sameAs\":[\"https:\\\/\\\/jamesbritt.com\\\/\",\"https:\\\/\\\/neurogami.com\\\/\",\"https:\\\/\\\/www.oreilly.com\\\/pub\\\/au\\\/2595\",\"https:\\\/\\\/www.rubyevents.org\\\/profiles\\\/james-britt\"],\"url\":\"https:\\\/\\\/ruby-doc.org\\\/learn\\\/james-britt\\\/\",\"jobTitle\":\"Ruby developer and writer\",\"knowsAbout\":[\"Ruby programming language\",\"Ruby documentation\",\"JRuby\",\"Open Sound Control\",\"Software development\"],\"affiliation\":{\"@type\":\"Organization\",\"name\":\"Ruby-Doc.org\",\"url\":\"https:\\\/\\\/ruby-doc.org\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Business Process Automation With Ruby: A Practical Guide","description":"James Britt explains how to build business process automation with Ruby using background jobs, idempotency, audit logs and human approval.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/","og_locale":"en_US","og_type":"article","og_title":"Business Process Automation With Ruby: A Practical Guide","og_description":"James Britt explains how to build business process automation with Ruby using background jobs, idempotency, audit logs and human approval.","og_url":"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/","og_site_name":"Ruby-Doc Learn","article_published_time":"2026-09-04T08:43:22+00:00","article_modified_time":"2026-09-04T14:00:06+00:00","og_image":[{"width":1659,"height":948,"url":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/ruby-business-process-automation.png","type":"image\/png"}],"author":"James Britt","twitter_card":"summary_large_image","twitter_misc":{"Written by":"James Britt","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/#article","isPartOf":{"@id":"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/"},"author":{"@id":"https:\/\/ruby-doc.org\/learn\/james-britt\/#james-britt"},"headline":"Business Process Automation With Ruby: From Script to Governed Workflow","datePublished":"2026-09-04T08:43:22+00:00","dateModified":"2026-09-04T14:00:06+00:00","mainEntityOfPage":{"@id":"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/"},"wordCount":1394,"publisher":{"@id":"https:\/\/ruby-doc.org\/learn\/#organization"},"image":{"@id":"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/#primaryimage"},"thumbnailUrl":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/ruby-business-process-automation.png","articleSection":["Programming"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/","url":"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/","name":"Business Process Automation With Ruby: A Practical Guide","isPartOf":{"@id":"https:\/\/ruby-doc.org\/learn\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/#primaryimage"},"image":{"@id":"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/#primaryimage"},"thumbnailUrl":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/ruby-business-process-automation.png","datePublished":"2026-09-04T08:43:22+00:00","dateModified":"2026-09-04T14:00:06+00:00","description":"James Britt explains how to build business process automation with Ruby using background jobs, idempotency, audit logs and human approval.","breadcrumb":{"@id":"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/#primaryimage","url":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/ruby-business-process-automation.png","contentUrl":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/ruby-business-process-automation.png","width":1659,"height":948,"caption":"A dependable Ruby automation needs controlled execution, human approval and a usable audit trail."},{"@type":"BreadcrumbList","@id":"https:\/\/ruby-doc.org\/learn\/business-process-automation-with-ruby\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ruby-doc.org\/learn\/"},{"@type":"ListItem","position":2,"name":"Business Process Automation With Ruby: From Script to Governed Workflow"}]},{"@type":"WebSite","@id":"https:\/\/ruby-doc.org\/learn\/#website","url":"https:\/\/ruby-doc.org\/learn\/","name":"Ruby-Doc Learn","description":"","publisher":{"@id":"https:\/\/ruby-doc.org\/learn\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/ruby-doc.org\/learn\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/ruby-doc.org\/learn\/#organization","name":"Ruby-Doc Learn","url":"https:\/\/ruby-doc.org\/learn\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ruby-doc.org\/learn\/#\/schema\/logo\/image\/","url":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/ruby-doc-logo-transparent.png","contentUrl":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/ruby-doc-logo-transparent.png","width":1650,"height":305,"caption":"Ruby-Doc Learn"},"image":{"@id":"https:\/\/ruby-doc.org\/learn\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/ruby-doc.org\/learn\/james-britt\/#james-britt","name":"James Britt","image":"https:\/\/ruby-doc.org\/learn\/wp-content\/uploads\/2026\/09\/jgb_self-portrait-20140914.png","description":"Creator and long-term maintainer of Ruby-Doc.org, founder of Neurogami, and author of practical Ruby tutorials for Ruby-Doc Learn.","sameAs":["https:\/\/jamesbritt.com\/","https:\/\/neurogami.com\/","https:\/\/www.oreilly.com\/pub\/au\/2595","https:\/\/www.rubyevents.org\/profiles\/james-britt"],"url":"https:\/\/ruby-doc.org\/learn\/james-britt\/","jobTitle":"Ruby developer and writer","knowsAbout":["Ruby programming language","Ruby documentation","JRuby","Open Sound Control","Software development"],"affiliation":{"@type":"Organization","name":"Ruby-Doc.org","url":"https:\/\/ruby-doc.org\/"}}]}},"_links":{"self":[{"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/posts\/1288","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/comments?post=1288"}],"version-history":[{"count":2,"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/posts\/1288\/revisions"}],"predecessor-version":[{"id":1464,"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/posts\/1288\/revisions\/1464"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/media\/1287"}],"wp:attachment":[{"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/media?parent=1288"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/categories?post=1288"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ruby-doc.org\/learn\/wp-json\/wp\/v2\/tags?post=1288"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}