• This Blog on Ruby 3.3.0

    Per tradition, this Christmas a new version of ruby was released.

    It promises performance improvements, especially with the YJIT turned on. Let’s see how it does using jekyll the static site generator for this very blog.

    Before (ruby 3.2.2 without YJIT)

    $ ruby -v
    ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]
    
    $ jekyll build
    Configuration file: /Users/ylansegal/Personal/blog/_config.yml
                Source: /Users/ylansegal/Personal/blog/src
           Destination: /Users/ylansegal/Personal/blog/_site
     Incremental build: disabled. Enable with --incremental
          Generating...
           Jekyll Feed: Generating feed for posts
                        done in 2.676 seconds.
     Auto-regeneration: disabled. Use --watch to enable.
    
    $ jekyll build
    [...]
                        done in 2.685 seconds.
    $ jekyll build
    [...]
                        done in 2.679 seconds.
    

    Average: 2.68 seconds

    After (ruby 3.3.0 with YJIT)

    $ ruby -v
    ruby 3.3.0 (2023-12-25 revision 5124f9ac75) +YJIT [arm64-darwin23]
    
    $ jekyll build
    Configuration file: /Users/ylansegal/Personal/blog/_config.yml
                Source: /Users/ylansegal/Personal/blog/src
           Destination: /Users/ylansegal/Personal/blog/_site
     Incremental build: disabled. Enable with --incremental
          Generating...
           Jekyll Feed: Generating feed for posts
                        done in 2.079 seconds.
     Auto-regeneration: disabled. Use --watch to enable.
    
    $ jekyll build
    [..]
                        done in 2.212 seconds.
    
    $ jekyll build
    [...]
                        done in 2.163 seconds.
    

    Average: 2.15 seconds

    Of course this is unscientific, but 20% local reduction in build time is impressive!

    Read on →

  • The REPL: Issue 111 - November 2023

    River: a Fast, Robust Job Queue for Go + Postgres

    I don’t write go-lang, and don’t have any insights into this new queue. The introduction of why a database based queue solves the dual-write problem are clear and applicable to any other system. Transactionality is one of the main reasons that I recommend GoodJob. The other one, also discussed is that operationally, having less data-stores is a win.

    A number of Postgres improvements also make it more performant than previous attempts at using db-backed job queues.

    Coincidentally, I was listening to a recent podcasts. They were discussing Redis as a store for background queuing. I was disappointed that they didn’t mention the dual-write problem as a pro of relational database queues.

    Rob Pike’s 5 Rules of Programming

    Concise and interesting. I’ve internalized as a best practice avoiding premature optimization. Rule 5 has me thinking a bit. Most web application programming doesn’t need to think much about data structures in memory, rather about how to design the database schema for storage.

    Read on →

  • The REPL: Issue 110 - October 2023

    Postgres Goodies in Ruby on Rails 7.1

    Rails 7.1 is out with some very interesting features for Potsgres users. Composite primary keys support in particular caught my eye: When partitioning tables, using a composite primary key that includes the partition key is a best practice. Now, Rails supports the composite primary keys in the model and associations (through query_constraints) ensuring that when reading from the table, the partition key is always used.

    Improved support for CTEs is also welcome!

    Writing Object Shape friendly code in Ruby

    It turns out that the way you structure classes (and more precisely variable instantiation) in ruby > 3.2 has performance implications. Ben Sheldon discusses how to structure classes to take advantage of those optimizations. It is an interesting demonstration on how code style and the ruby interpreter interact.

    The article doesn’t mention how much it impacts performance. I wonder: On a typical web request, how much can this save by structuring your classes for optimization?

    The TLDR on Ruby’s new TLDR testing framework

    It’s called TLDR and it blows up if your tests take more than 1.8 seconds to run.

    Testing is a near and dear topic to me. I have not tried this new framework, but I have some initial thoughts:

    • 1.8s is not a lot of time for a whole test suite.
    • Test that fast need to avoid database interactions at all costs. In my experience that leads to heavy mocking, which in turn can lead to unit test passing but the components break on integration.
    • TLDR seems incompatible with large systems (e.g. majestic monolith). For good or bad.
    • Pushing the envelope can lead to some great ideas. For example:

    TLDR automatically prepends the most-recently modified test file to the beginning of the suite

    This is brilliant. I have a script that guesses which test files to run on a branch based on what changed in git. After reading this, I immediately incorporated ordering the files by modification date.

    Read on →

  • Book Review: A Philosophy of Software Design

    by John Ousterhout

    This book focuses on software design, identified as a continuos process that spans the complete lifecycle of a software system. The first part of the book proposes that the main issue in software design lies in managing complexity.

    If a software system is hard to understand and modify, then it is complicated; if it is easy to understand and modify, then it is simple.

    The rest of the book are a collection of principles, techniques, and heuristics to help remove or hide the complexity, replacing it with a simper design.

    It is easier to tell whether a design is simple than it is to create a simple design,

    Probably the most salient piece of advise is that “modules should be deep”: A module is deep when it provides a narrow interface to it’s callers that provides a lot of functionality that abstracts away the details of the implementation.

    Adding garbage collection to a system actually shrinks its overall interface, since it eliminates the interface for freeing objects. The implementation of a garbage collector is quite complex, but that complexity is hidden from programmers.

    Overall, I found the books worthwhile. Especially the attitude that the overall design of a system is constantly shifting. Individual programmers add or remove to the complexity in small increments every time they make changes to the system. Cutting corners very often, will leave the code in a state that is hard to recover from.

    My own attitudes to software design align well with Ousterhout’s except for comments and tests. The author uses comments as a design aid: Writing interface comments first, before implementing any code, so that they guide the design. It gets the programmer thinking about how the module will be used, instead of how it will be implemented. As for tests:

    The problem with test-driven development is that it focuses attention on getting specific features working, rather than finding the best design.

    I wholeheartedly agree with the goal of writing comments first: Outside-in thinking results in better design. Focusing on how a module will be used from a caller’s perspective improves the module’s API. Sometimes comments can serve that purpose, but I think that the author misses the point that test-driven design (TDD) accomplishes that purpose as well. When you write your tests first, by definition you are forced to think how the module will be called, because the test itself uses it! In fact, TDD works best when you start writing tests in the outermost layer of your system and work your way inwards. It gets some time getting use to because the outermost test wont pass until the innermost implementation is complete. The gain is that those tests inform the design through the layers. As for the criticism about TDD being to focused on getting specific features working, I think this is a “shallow” TDD. TDD is typically a red-green-refactor loop. Red: write a failing test. Green: make it pass. Refactor: improve the design. I would agree with Ousterhout if we stopped at red-green, but the last step, the refactor, is what makes it complete: Red improves the API design, Green makes it correct, Refactor improves the internal design.

    Links:

    Read on →

  • The REPL: Issue 109 - September 2023

    YJIT is juicy!

    I’ve seen some recent post on social media about the great performance of Ruby + YJIT. It’s time to give a try!.

    I got it working locally with asdf:

    $ asdf install rust 1.72.1
    $ export ASDF_RUST_VERSION=1.72.1
    $ export RUBY_CONFIGURE_OPTS=--enable-yjit
    
    $ asdf install ruby 3.2.1
    $ asdf shell ruby 3.2.1
    $ ruby --yjit -v
    ruby 3.2.1 (2023-02-08 revision 31819e82c8) +YJIT [arm64-darwin22]
    

    Why You Might Not Want to Run Rails App:update

    The author points out that rails app:update should be use with caution, because it might make unwanted changes to your application or remove manually added configuration. Fair enough. What I don’t understand is the remedy: not to use it! That is what version control is for! I’ve upgraded multiple apps, multiple times using rails app:update. In every case, before committing the changes to version control I inspect each one and make an informed decision if I want to keep them or not.

    Read on →