Python vs Ruby - I built the same github analyzer with both

Published: (January 18, 2026 at 06:00 PM EST)
6 min read
Source: Dev.to

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:

AspectPythonRuby
Dependency ManagementVirtualenv + pip / Poetry / PipenvBundler (Gemfile, Gemfile.lock)
SyntaxIndentation‑driven, explicit returnsend‑driven, implicit returns
HTTP Libraryrequests (function‑based)HTTParty (module inclusion)
Data ProcessingPandas (high‑level, fast)Enumerables (manual, expressive)
Web FrameworkFlask (micro‑framework)Sinatra (micro‑framework)
StrengthsData science, scientific computingWeb development, DSLs, developer joy
WeaknessesPackaging fragmentationSmaller 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!

Back to Blog

Related posts

Read more »

Go Away Python

Article URL: https://lorentz.app/blog-item.html?id=go-shebang Comments URL: https://news.ycombinator.com/item?id=46431028 Points: 27 Comments: 8...

A Eficiência do Cache no SQLite

A Eficiência do Cache no SQLite !Cover image for A Eficiência do Cache no SQLitehttps://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=aut...