惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fortinet All Blogs
爱范儿
爱范儿
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
B
Blog
WordPress大学
WordPress大学
Jina AI
Jina AI
GbyAI
GbyAI
aimingoo的专栏
aimingoo的专栏
N
Netflix TechBlog - Medium
腾讯CDC
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
阮一峰的网络日志
阮一峰的网络日志
The GitHub Blog
The GitHub Blog
V
Visual Studio Blog
Google DeepMind News
Google DeepMind News
月光博客
月光博客
博客园 - Franky
Y
Y Combinator Blog
MyScale Blog
MyScale Blog
大猫的无限游戏
大猫的无限游戏
Martin Fowler
Martin Fowler
雷峰网
雷峰网
小众软件
小众软件
H
Hackread – Cybersecurity News, Data Breaches, AI and More

Hacker News - Newest: "LLM"

GitHub - lechmazur/position_bias: A benchmark for testing whether LLM judges keep the same preference when two lightly edited versions of the same story are shown in opposite orders. Flex routing (EU and EFTA) Dark Factories: Retooling for LLM Velocity Ask HN: What would be the impact of a LLM output injection attack? GitHub - Oaklight/llm-rosetta: Production-ready LLM API translation layer for Python — bidirectional conversion between OpenAI, Anthropic & Google formats via hub-and-spoke IR. Optional API gateway. Streaming & non-streaming. Zero core deps. Contributions welcome! GitHub - browser-use/browser-harness: Self-healing browser harness that enables LLMs to complete any task. GitHub - moeen-mahmud/remen: Remen turns thoughts into something you can return to Analyzing 156 LLM Launch Posts on Hacker News ChatGPT vs Gemini vs Claude: The Best LLM Subscription You Should Buy GitHub - salaamalykum/quran-semantic-search: High-density RAG Semantic Search Engine & Quran Corpus (GEO/SEO Architecture) GitHub - NVIDIA/TensorRT-LLM: TensorRT LLM provides users with an easy-to-use Python API to define Large Language Models (LLMs) and supports state-of-the-art optimizations to perform inference efficiently on NVIDIA GPUs. TensorRT LLM also contains components to create Python and C++ runtimes that orchestrate the inference execution in a performant way. The State of LLM Bug Bounties in 2026 Operational Readiness Criteria for Tool-Using LLM Agents Meshcore: Architecture for a Decentralized P2P LLM Inference Network How an LLM becomes more coherent as we train it GitHub - seetrex-ai/laimark GitHub - Jossifresben/BibCrit: AI-assited biblical textual criticism GitHub - wastedcode/memex: File system based wiki, maintained by Claude 99helpers.com GitHub - cliver-project/AITrigram GitHub - unbody-io/adapt: A self-evolving memory layer for AI agents. GitHub - hb20007/awesome-gen-ai-fails: A list of incidents where reliance on generative AI and LLMs resulted in harm to companies, individuals, or society GitHub - nevenkordic/localmind: Run any local LLM with persistent memory and context. CLI agent over Ollama with SQLite-backed hybrid recall. No cloud. Ask HN: What are the machine requirements for a LLM like Llama-3.1-8B? Faster LLM Inference via Sequential Monte Carlo grpo explained: group relative policy optimization for llm finetuning - cgft Stop comparing price per million tokens: the hidden LLM API costs · TensorZero Andrej Karpathy's LLM Wiki Is a Bad Idea GitHub - GG-QandV/mnemostroma: Offline RAM-first cognitive leer/coprocessor for AI agents and robotics. Solves "Context Abandonment" with 20-80ms latency using a dual-thread biomimetic memory architecture (ONNX + SQLite WAL). mempalace/agent at agent · skorotkiewicz/mempalace
Why Ruby Is the Better Language for LLM-Powered Development
faangguyindi · 2026-05-14 · via Hacker News - Newest: "LLM"

When an LLM writes your code, different things matter than when a human does. You stop caring about IDE autocomplete quality and start caring about token count. You stop debating type systems and start measuring first-pass correctness. You stop choosing languages for their ecosystem and start choosing them for how efficiently they compress into — and out of — a context window.

We have been building with LLMs writing significant portions of our production code for the past year. We tried TypeScript, Python, and Ruby. Ruby won, and it was not close.

The data

We wrote the same four features — a CRUD controller, a data transformation pipeline, a test suite, and a background job — in Ruby, TypeScript, and Python. Identical functionality. Then we tokenized all twelve files with both OpenAI's tokenizer (used by GPT-4o, GPT-4.1, o3, and o4-mini) and Anthropic's tokenizer (used by Claude Sonnet 4 and Opus 4).

OpenAI tokenizer (o200k_base)

Example Ruby TypeScript Python Ruby vs TS Ruby vs Py
CRUD controller 230 361 406 -36% -43%
Data pipeline 174 371 203 -53% -14%
Test suite 338 458 377 -26% -10%
Background job 165 368 270 -55% -39%
Total 907 1,558 1,256 -42% -28%

Claude tokenizer (Sonnet 4 / Opus 4)

Example Ruby TypeScript Python Ruby vs TS Ruby vs Py
CRUD controller 269 460 508 -42% -47%
Data pipeline 220 473 248 -53% -11%
Test suite 390 586 482 -33% -19%
Background job 218 481 373 -55% -42%
Total 1,097 2,000 1,611 -45% -32%

Ruby saves 42% of tokens on OpenAI and 45% on Claude compared to TypeScript. Against Python, it saves 28% and 32% respectively. The savings are actually larger on Claude's tokenizer because Claude uses more tokens for code overall, which makes Ruby's structural conciseness matter more.

Where the tokens go

The savings are not a tokenizer quirk. They are structural. Look at the data pipeline example — the most dramatic difference at 53% fewer tokens across both tokenizers.

Ruby:

class OrderReport
  def initialize(orders)
    @orders = orders
  end

  def generate
    @orders
      .select { |o| o.status == "completed" && o.total > 0 }
      .group_by { |o| o.created_at.strftime("%Y-%m") }
      .transform_values do |monthly_orders|
        {
          count: monthly_orders.size,
          revenue: monthly_orders.sum(&:total),
          average: monthly_orders.sum(&:total) / monthly_orders.size.to_f,
          top_product: monthly_orders
            .flat_map(&:line_items)
            .tally_by(&:product_name)
            .max_by { |_, count| count }
            &.first
        }
      end
      .sort_by { |month, _| month }
      .to_h
  end
end

174 tokens on GPT-4o. 220 on Claude. Now the TypeScript equivalent:

import { Order, LineItem } from "./types";

interface MonthlyReport {
  count: number;
  revenue: number;
  average: number;
  topProduct: string | undefined;
}

export function generateOrderReport(
  orders: Order[]
): Record<string, MonthlyReport> {
  const completed = orders.filter(
    (o) => o.status === "completed" && o.total > 0
  );

  const grouped = new Map<string, Order[]>();
  for (const order of completed) {
    const month = order.createdAt.toISOString().slice(0, 7);
    const existing = grouped.get(month) ?? [];
    existing.push(order);
    grouped.set(month, existing);
  }

  const result: Record<string, MonthlyReport> = {};
  const sortedKeys = [...grouped.keys()].sort();

  for (const month of sortedKeys) {
    const monthlyOrders = grouped.get(month)!;
    const revenue = monthlyOrders.reduce((sum, o) => sum + o.total, 0);

    const productCounts = new Map<string, number>();
    for (const order of monthlyOrders) {
      for (const item of order.lineItems) {
        productCounts.set(
          item.productName,
          (productCounts.get(item.productName) ?? 0) + 1
        );
      }
    }

    let topProduct: string | undefined;
    let maxCount = 0;
    for (const [name, count] of productCounts) {
      if (count > maxCount) {
        maxCount = count;
        topProduct = name;
      }
    }

    result[month] = {
      count: monthlyOrders.length,
      revenue,
      average: revenue / monthlyOrders.length,
      topProduct,
    };
  }

  return result;
}

371 tokens on GPT-4o. 473 on Claude. Same output. The TypeScript version needs import statements, an interface definition, explicit type annotations on every variable, manual Map construction instead of group_by, manual iteration instead of transform_values, and a manual max-finding loop instead of max_by.

None of that is bad TypeScript. It is idiomatic, well-typed, clean code. It just takes twice as many tokens to say the same thing.

Why fewer tokens matters more than you think

The obvious argument is cost. At GPT-4o rates ($2.50/1M input, $10/1M output), a team of five developers doing 20 LLM sessions per day saves roughly $40/month by using Ruby instead of TypeScript. That is not life-changing.

The real argument is context window budget. Every LLM has a fixed context window — 128K tokens for GPT-4o, 200K for Claude. When you send code to an LLM, you are spending from that budget. When the LLM sends code back, it spends more. When you include project context, documentation, or examples, that spends even more.

A 42% reduction in token usage per code block means 42% more room for everything else. More files in context. Longer conversations before the model starts forgetting. More examples in your prompts. That is the difference between an LLM that understands your codebase and one that lost track three messages ago.

And there is a compounding effect: when the LLM generates Ruby, the output is also shorter. The model finishes faster, costs less, and leaves more room in the context for the next round-trip. Over a multi-turn coding session, the savings stack.

RSpec: the best testing story for LLM output

When an LLM generates code, you need to verify it works. This is where Ruby's testing ecosystem — specifically RSpec — becomes a genuine competitive advantage.

RSpec.describe LlmCodeGenerator do
  subject(:generator) { described_class.new(model: "gpt-4o") }

  describe "#generate_migration" do
    let(:prompt) { "Create a posts table with title, body, and published_at" }
    let(:result) { generator.generate_migration(prompt) }

    it "produces valid Ruby syntax" do
      expect { RubyVM::InstructionSequence.compile(result.code) }.not_to raise_error
    end

    it "includes all requested columns" do
      expect(result.code).to include("t.string :title")
        .and include("t.text :body")
        .and include("t.datetime :published_at")
    end

    context "when the model hallucinates a gem dependency" do
      before { allow(generator).to receive(:raw_response).and_return(hallucinated_response) }

      it "strips require statements for unknown gems" do
        expect(result.code).not_to match(/^require/)
      end
    end

    context "with token budget tracking" do
      it "stays within budget" do
        expect(result.tokens_used).to be < 500
      end

      it "costs less than a penny" do
        expect(result.cost_usd).to be < 0.01
      end
    end
  end
end

338 tokens. Compare to the Vitest equivalent at 458 tokens — 26% more for the same coverage. But the token count is secondary to what makes RSpec actually better for this use case:

Composable matchers. expect(result.code).to include("t.string :title").and include("t.text :body") chains multiple assertions in a single readable line. In Vitest, that is three separate expect().toContain() calls, each awaiting the same async result.

Context blocks as specifications. RSpec's context "when the model hallucinates" reads like a specification document. When you are defining expected behavior for non-deterministic LLM output, this structure matters — you are writing a contract, not a test.

Lazy evaluation with let. let(:result) { generator.generate_migration(prompt) } is evaluated once per test, on first access. No beforeEach boilerplate, no repeated async calls, no stale variable bugs.

Built-in syntax validation. RubyVM::InstructionSequence.compile(result.code) checks that generated Ruby code is syntactically valid without executing it. TypeScript requires spawning a tsc process or using new Function() which only validates JavaScript, not TypeScript.

Ruby syntax is closer to natural language

LLMs are language models. They were pretrained on trillions of tokens of natural language before they ever saw code. Ruby's syntax is closer to English than any mainstream language:

5.times do
  order.fulfill! if order.paid?
end

users.select(&:active?).sort_by(&:created_at).first(10)

retry_on Net::TimeoutError, wait: :polynomially_longer, attempts: 5

Every line reads like a sentence. retry_on TimeoutError, wait polynomially_longer, attempts 5 — that is almost English. Compare to the TypeScript equivalent: backoff: { type: "exponential", delay: 1000 } inside a configuration object three levels deep.

This is not an aesthetic preference. It means LLMs produce correct Ruby on the first attempt more often than they produce correct TypeScript. The code is closer to the natural language description the model works from internally. Fewer tokens to express intent means fewer places for the model to diverge from what you asked for.

The counterarguments

"TypeScript has type safety." It does, and types are valuable for human developers navigating large codebases. But LLMs do not need type annotations to generate correct code — they infer types from context, variable names, and usage patterns. Type annotations are for humans and compilers. To an LLM, name: string is redundant information that costs tokens. A parameter called name is obviously a string.

"Python has the ML ecosystem." True. PyTorch, scikit-learn, and transformers are Python-first. But you do not build your web application in the same language you train models in. Use Python for training and data science. Use Ruby for the product that talks to the LLM API.

"Ruby is slower." With YJIT (Ruby 3.x) and ZJIT (Ruby 4.0), Ruby is fast enough for any web application. Your bottleneck is the database and the LLM API response time, not the language runtime. A 200ms API call to GPT-4o dwarfs any difference between Ruby and TypeScript execution speed.

"Nobody uses Ruby anymore." Ruby has a smaller community than TypeScript or Python, which is actually an advantage for LLM-generated code. The Ruby ecosystem is more stable and opinionated — there is usually one good way to do things (Rails, RSpec, Sidekiq/Solid Queue). LLMs generate more consistent Ruby because there are fewer competing patterns to confuse the model. Ask an LLM to write a TypeScript API and you might get Express, Fastify, Hono, Koa, or NestJS. Ask it to write a Ruby API and you get Rails.

The economics

Here is a concrete scenario. A team of five developers, each running 20 LLM-assisted coding sessions per day, averaging 800 input tokens and 400 output tokens per session (in Ruby token counts):

Language Daily input Daily output Monthly cost
Ruby (baseline) 16,000 8,000 $2.64
Python (1.3x) 20,800 10,400 $3.43
TypeScript (1.7x) 27,200 13,600 $4.49

At GPT-4o rates. For Claude, multiply by roughly 1.2x across the board (Claude's tokenizer produces more tokens for code, so the absolute costs are higher, but Ruby's percentage savings are also larger).

The monthly dollar difference is small. The context window difference is not. Every TypeScript session burns 70% more of the context budget than the same Ruby session. Over a multi-turn conversation with file context, that is the difference between the model seeing your whole feature and the model losing track of your data model halfway through.

What we actually do

We are not theorizing. This is how we build software at Bytecode. Our production stack is Ruby on Rails with RSpec. When we use LLMs to generate code — and we do, extensively — the code is Ruby. The token efficiency of Ruby was not why we chose it originally, but it is a meaningful advantage now that LLMs are part of our daily workflow.

Our fine-tuned Rails models

We went further than just choosing Ruby. We fine-tuned our own models specifically for Ruby on Rails code generation, trained on 111,000 samples extracted from our own internal Rails projects. The models are open and available on Hugging Face:

  • qwen3-coder-30b-rails — 31B parameter MoE model. Available in Q4_K_M (18.6 GB) and Q5_K_M (21.7 GB) GGUF formats. This is the flagship: it writes idiomatic Rails code that follows our conventions out of the box.

  • qwen3-8b-rails — 8B parameter dense model. Q4_K_M at just 5 GB. Runs on a laptop with 8 GB of RAM. Fast enough for inline code completion, small enough to run locally alongside your dev server.

Both models run locally with Ollama:

ollama run bytecodehr/qwen3-8b-rails

The combination is what matters: Ruby's 42–45% token savings mean our models can fit more context into the same window, and the fine-tuning means the code they generate already follows our patterns — Devise authentication, namespaced concerns, Sidekiq, state-as-records. No prompt engineering needed to avoid the generic defaults that general-purpose models fall back on.

The full story of how we built the dataset and trained the models is in Part 1: Dataset Engineering and Part 2: Training, Quantization, and Deployment.

Conclusion

If you are starting a new project and you plan to use LLMs as a core part of your development process, consider the language you are asking them to think in. Ruby is shorter, more expressive, closer to natural language, and has the best testing story for validating AI-generated output. The token savings are real and they compound.

And if you want models that already speak fluent Rails, we built those too.