Python vs Ruby - I built the same github analyzer with both
Source: Dev.to
1. Setting Up the Environment
Python
We need to isolate our dependencies. If you are a beginner, you Google “how to install packages,” see pip install, and run it—only to end up installing a library globally and breaking your OS‑level Python.
So, you learn about virtual environments:
python -m venv venv
source venv/bin/activate
You create a requirements.txt file. But requirements.txt is limited; it doesn’t handle dependency resolution well. That’s why many people turn to Poetry, Pipenv, Conda, or Rye. The Python ecosystem is currently in a civil war regarding packaging. It works, but it feels like assembling IKEA furniture without the instructions.
Ruby
Ruby has had this solved for over a decade. It’s called Bundler.
Create a Gemfile:
source 'https://rubygems.org'
gem 'sinatra'
gem 'httparty'
Then run:
bundle install
Bundler manages your dependencies, locks the versions in a Gemfile.lock, and ensures that if I send this code to you, it works exactly the same way. It is the gold standard that every other language—including JavaScript’s npm and Rust’s Cargo—tried to copy.
2. Language Syntax
Python – Significant Whitespace
Python uses indentation to define blocks of code:
def analyze_repo(user, repo):
if user:
print(f"Analyzing {repo}...")
else:
print("No user provided.")
If you mess up the indentation by even one space, the program crashes with an IndentationError. This forces you to write clean, consistently indented code.
Ruby – end Keywords
Ruby doesn’t care about whitespace; it cares about keywords, especially end:
def analyze_repo(user, repo)
if user
puts "Analyzing #{repo}..."
else
puts "No user provided."
end
end
No colons, no required indentation (though you should indent). You simply close every block with end.
Return Values
Python is explicit:
def greet(name):
return f"Hello, {name}"
Ruby returns the value of the last evaluated expression automatically:
def greet(name)
"Hello, #{name}"
end
Python looks like a blueprint; Ruby reads more like a sentence.
3. Calling the GitHub API
Python – requests
import requests
def get_commits(user, repo):
url = f"https://api.github.com/repos/{user}/{repo}/commits"
response = requests.get(url)
return response.json()
requests is a tool we use.
Ruby – HTTParty
require 'httparty'
class GithubFetcher
include HTTParty
base_uri 'https://api.github.com'
def self.get_commits(user, repo)
get("/repos/#{user}/#{repo}/commits")
end
end
HTTParty is a module we include, extending the class with HTTP capabilities. This showcases Ruby’s object‑oriented nature: everything is an object, and classes are open for modification.
4. Data Processing
Python – Pandas (the king of data)
import pandas as pd
def analyze_commits(commits):
df = pd.json_normalize(commits)
# Example: count commits per author
author_counts = df['author.login'].value_counts()
# Example: busiest day of week
df['date'] = pd.to_datetime(df['commit.author.date'])
busiest_day = df['date'].dt.day_name().value_counts().idxmax()
return author_counts, busiest_day
Pandas lets us write concise, high‑performance data pipelines.
Ruby – Pure Enumerables
require 'date'
def analyze_commits(commits)
# Count commits per author
author_counts = commits.each_with_object(Hash.new(0)) do |c, h|
author = c.dig('author', 'login') || 'unknown'
h[author] += 1
end
# Busiest day of week
days = commits.map do |c|
date_str = c.dig('commit', 'author', 'date')
Date.parse(date_str).strftime('%A') if date_str
end.compact
busiest_day = days.tally.max_by { |_, count| count }&.first
[author_counts, busiest_day]
end
Ruby’s built‑in Enumerable methods are powerful, but they require more manual work compared to Pandas.
5. Web Frameworks
- Flask (Python) – Minimalist, explicit routing, easy to extend with extensions.
- Sinatra (Ruby) – Even more lightweight; routes are defined with simple DSL methods.
Both frameworks let you spin up a web UI for the analyzer in a few lines of code.
6. Performance & Ecosystem
- Performance: Ruby’s interpreter (MRI) is generally slower than CPython for raw CPU‑bound tasks, but both are fast enough for typical API‑driven web apps.
- Ecosystem: Python dominates data science, machine learning, and scientific computing. Ruby shines in web development, DSLs, and developer ergonomics.
7. Verdict
Both languages can build the same GitHub Repository Analyzer, but they do it in very different ways:
| Aspect | Python | Ruby |
|---|---|---|
| Dependency Management | Virtualenv + pip / Poetry / Pipenv | Bundler (Gemfile, Gemfile.lock) |
| Syntax | Indentation‑driven, explicit returns | end‑driven, implicit returns |
| HTTP Library | requests (function‑based) | HTTParty (module inclusion) |
| Data Processing | Pandas (high‑level, fast) | Enumerables (manual, expressive) |
| Web Framework | Flask (micro‑framework) | Sinatra (micro‑framework) |
| Strengths | Data science, scientific computing | Web development, DSLs, developer joy |
| Weaknesses | Packaging fragmentation | Smaller data‑science ecosystem |
Which one fits you?
- If you live in the data world, need heavy analytics, or prefer a language with a massive scientific ecosystem, Python is the clear choice.
- If you love elegant DSL‑style code, enjoy a unified dependency system, and want a language that treats everything as an object, Ruby will feel more natural.
Ultimately, the “best” language is the one that aligns with your project’s goals and your personal workflow. Choose wisely, and happy coding!
Loops in Python vs. Ruby
Python (using Pandas):
import pandas as pd
def process_commits(commits):
# Load data into a DataFrame
df = pd.DataFrame(commits)
# Normalize the nested JSON to get author names
df['author_name'] = df['commit'].apply(lambda x: x['author']['name'])
# Count commits by author
top_authors = df['author_name'].value_counts().head(5)
return top_authors.to_dict()
This is incredibly powerful. With three lines of code, I can process millions of rows. However, it is heavy. I had to install a massive library just to count some items. It feels like bringing a tank to a knife fight. It’s effective, but it’s overkill.
Ruby (pure Enumerables):
def process_commits(commits)
commits
.map { |c| c['commit']['author']['name'] } # Extract names
.tally # Count occurrences
.sort_by { |_, count| -count } # Sort descending
.take(5) # Get top 5
.to_h # Convert back to Hash
end
Look at that Ruby code. .map, .tally, .sort_by, .take. It reads like English instructions: “Map the names, tally them up, sort by count, take the top 5.” No external libraries are required—everything is built into the language. This is what Ruby fans mean when they say the language makes them happy.
Putting it on the web – Micro‑frameworks
Python (Flask) – a single‑file server:
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/analyze/<user>/<repo>/')
def analyze(user, repo):
data = get_commits(user, repo)
return jsonify(data)
Flask uses decorators, a very “Pythonic” concept. We wrap the function with metadata that tells the server “When this URL is hit, run this function.” It is explicit; you can see exactly what is happening.
Ruby (Sinatra) – the original inspiration:
require 'sinatra'
require 'json'
get '/analyze/:user/:repo' do
content_type :json
data = get_commits(params[:user], params[:repo])
data.to_json
end
Sinatra uses a DSL (Domain Specific Language). Notice we aren’t defining a class or a method in the traditional sense—we’re just typing get. This is Ruby’s superpower: metaprogramming. Library authors can change the language’s syntax to fit the domain, making Sinatra feel like you’re writing “HTTP” rather than Ruby.
However, this magic comes at a cost. If you don’t know Sinatra, you might wonder, “Where is the get method defined? Is it a global variable? A method on an object?” It’s implicit. Python’s decorators make it obvious that analyze is just a function.
Ecosystem considerations
Python
- Superpowers: NumPy (math), PyTorch/TensorFlow (AI), Django (web), Ansible (automation), BeautifulSoup (scraping).
- Versatility: Second‑best language for everything. Not the fastest or prettiest, but the go‑to for integrating web apps with machine‑learning models.
- Job market: Massive—Web, Data, Ops, Security, Academic Research.
Ruby
- Superpower: Web development, especially with Rails.
- Strengths: Rails enables small teams to build massive products (GitHub, Shopify, Twitch) incredibly fast.
- Limitations: Outside web dev, Ruby is quiet. Few (if any) AI, embedded‑systems, or scientific‑computing libraries.
- Career path: Primarily a Rails backend engineer—lucrative but narrower.
Final Verdict
- Choose Ruby if you value the art of coding, want rapid web‑app development, and enjoy a language that feels like it “writes itself.”
- Choose Python if you value utility, need a tool that can do anything—from web to AI—and want the safest bet for a long, versatile career.
If you’d like me to compare Go vs. Rust or anything else, let me know in the comments!