Polyglot programming is the practice of using multiple programming languages to build a single application, leveraging each language’s strengths. This approach involves running different parts of an application written in different languages and coordinating them to work together as one system.
1. HTTP Requests (REST API)
Best for: Synchronous communication (Request/Response) and standard web apps.
This is the industry standard. You turn one component (e.g., Python) into a “Server” and the other (Ruby) into a “Client.” The client sends data via a URL, and the server processes it and sends an answer back.
- The Python Side: You use a lightweight framework like Flask or FastAPI to listen on a local port (e.g.,
localhost:5000). - The Ruby Side: You use an HTTP client library like Faraday or HTTParty to send JSON data to that port.
Example flow: Ruby sends a
POSTrequest with{ "user_id": 5 }to Python. Python calculates a score and returns{ "score": 95 }. Ruby receives the score immediately.
2. Message Broker (Redis)
Best for: Asynchronous background jobs, high speed, or “Fire and Forget.”
If the two components don’t need to talk instantly (e.g., Python queues a job, Ruby processes it later) or if you need high-speed real-time messaging, Redis is the best local tool. It acts as a middleman.
- Shared Space: Both Python and Ruby connect to the same local Redis instance.
- Pub/Sub Pattern: Python “Publishes” a message to a channel (e.g.,
updates). Ruby “Subscribes” to that channel and reacts whenever a message arrives. - Queue Pattern: Python pushes an item into a list; Ruby pops it off and processes it.
3. Subprocesses (Shell Execution)
Best for: Simple scripts where one controls the other tightly.
If one script is the “Master” and just needs to run the other script to get a result, you don’t need networking. You can simply execute the other language’s command line interface.
- How it works: The Python script uses the
subprocesslibrary to runruby script.rb. - Data Transfer: Python sends data via Standard Input (stdin) and reads the result from Ruby’s Standard Output (stdout).
Note: This is simple to set up but hard to scale. If the Ruby script crashes or hangs, it can freeze the Python script.
4. Shared Files (File I/O)
Best for: Large data processing or legacy systems.
Both scripts agree on a specific file path (e.g., data.json). One writes to it, and the other reads from it.
- Constraint: You usually need a mechanism to tell the second script when the file is ready (like creating a secondary “lock” file or using a file-watcher library).
- Downside: This is generally the slowest method and prone to “race conditions” (where one reads while the other is still writing).
| Method | Complexity | Speed | Best Use Case |
|---|---|---|---|
| HTTP (REST) | Medium | Medium | Standard services, easy to debug. |
| Redis | Low (requires install) | Very High | Real-time data or background queues. |
| Subprocess | Low | High | One script simply “running” the other. |
| Shared Files | Low | Low | Large batch processing. |
Simple Example with Python (FastAPI) and Ruby (Faraday) Run them in separate terminals.
from fastapi import FastAPI
from pydantic import BaseModel
import uvicorn
app = FastAPI()
# 1. Define the data shape we expect from Ruby
class Numbers(BaseModel):
x: int
y: int
@app.get("/")
def read_root():
return {"status": "Python server is running"}
# 2. Create the endpoint that receives data
@app.post("/add")
def add_numbers(data: Numbers):
result = data.x + data.y
print(f"Received {data.x} and {data.y} from Ruby. Result: {result}")
return {"operation": "addition", "result": result}
if __name__ == "__main__":
# Run the server on localhost port 8000
uvicorn.run(app, host="127.0.0.1", port=8000)
require 'faraday'
require 'json'
# 1. Configure the connection to the Python app
url = 'http://127.0.0.1:8000'
conn = Faraday.new(url: url)
puts "--- Ruby Client Starting ---"
# 2. Prepare the payload
payload = { x: 50, y: 100 }
# 3. Send the POST request
response = conn.post('/add') do |req|
req.headers['Content-Type'] = 'application/json'
req.body = payload.to_json
end
# 4. Handle the response
if response.status == 200
data = JSON.parse(response.body)
puts "Success! Python answered:"
puts "Result: #{data['result']}"
else
puts "Error: #{response.status}"
end
They can also be containerized with Docker so they can communicate without needing manual terminal setups.