Ylan Segal

The REPL: Issue 44 - March 2018

Mistakes Rails Developers Make in Elixir Part 1: Background Jobs

Background jobs in Rails are a common patter. In this post Desmond Bowe explores some of the available patterns in Elixir that can be used instead of reaching for a background queue. The information is very good. In my experience, every time I reach for background jobs, I also need to ensure that jobs survive node crashes. For that, the author still advises to use a traditional background queue.

A Career Cold Start Algorithm

Andrew Bosworth (Boz) advocates a simple way to start a new job: Ask everyone what is it that they think that you need to know, what are their challenges and who should you talk to next. This is a great idea, especially in places where a robust knowledge transfer process is not in place.

Elapsed time with Ruby, the right way

Luca Guidi explains why the naive use of Time.now to measure elapsed time between starting and ending an expensive operation is wrong. What to use? Monotonic time.

Remember: wall clock is for telling time, monotonic clock is for measuring time.

The REPL: Issue 43 - February 2018

What Does OO Afford?

Sandi Metz is one of my favorite authors, and for good reason. In this post, she talks about affordances (a made up word!) that programming languages give, in particular Object-Oriented languages:

OO… wants you to replace your procedural monoliths with collections of small, independent, collaborative objects.

The Lost Art of the Makefile

Jesse Hallett walks through the basics of make and Makefiles. He talks about targets, how it excels at managing dependencies and how it can be leveraged for a modern Javascript workflow. I don’t write Javascript, but I have found make to be very useful for Crystal projects. I’ve even seen people use it for ruby development.

Book Review: Functional Web Development With Elixir

Functional Web Development with Elixir, OTP, and Phoenix: Rethink the Modern Web App by Lance Halvorsen covers how to build web application in Elixir leveraging it’s great concurrency properties using OTP. Throughout the book, the author guides the reader through building an application. At first, the focus is only on the business logic, outside from any web-framework. Later, the author covers how to use that code inside a Phoenix application without the tight coupling that often results in other web frameworks. Phoenix web views are largely ignored, focusing instead on it’s finest features: Channels and Presence.

Considerably time is spent discussing OTP and GenServers in particular. I feel I have a relatively good understanding, while at the same time feeling that something hasn’t quite “sunk in” yet. It was also great to learn the basics of supervision trees, ETS (the in-memory storage built-in to the BEAM), and MNESIA (a relational database that is also built-in that doesn’t use SQL).

The app is built chapter by chapter, in a bottom-up approach. While the reader does end up with a functioning application, it is easy to miss the forest for the trees. Especially, since most readers won’t have OTP experience and don’t know how those applications are structured. I would have much rather have seen an outside-in approach, especially one driven by tests.

I was also disappointed that testing was not even mentioned in the book. I consider testing to be an integral part of my day-to-day work and can’t really evaluate a framework or language without knowing what the ergonomics of testing are like. In particular I am left wondering how to test asynchronous code, channels or presence modules, especially when it requires passing Socket structures to functions.

In my other explorations of Elixir, I’ve read about umbrella applications being a great way to keep modules separate. While the author spends considerable time on the same subject, he doesn’t mention umbrella applications (either negatively or positively).

Finally, while I enjoyed the book, I did find that there seems to be some implicit assumption that Elixir scales very well across nodes, but I wish there was more of an explanation of how computation is distributed among Elixir/Erlang nodes and in particular how is state propagated between them when using something like ETS.

Links:

The REPL: Issue 42 - January 2018

SPAs Are Just Harder, And Always Will Be

William Gross points out that Single-Page Applications add more development overhead than traditional server-rendered applications. There are more layers to code and maintain and essentially add to each browser the burden of distributed computation and data synchronization.

Things I wish ActiveRecord had after using Ecto

Ecto is a database wrapper library for Elixir. It’s design is very different that ActiveRecord – the Ruby library included with Ruby on Rails. They are often compared, because each seems to hold the majority of mindshare in it’s own ecosystem and there is a significant portion of the Ruby community interested in Elixir.

Vladimir Rosančić walks through the things he likes about Ecto that are missing from ActiveRecord. He names changesets, database constrain validation, explicit pre-loading, batch inserts, safety when loading single records and the query language itself. I find these type of comparisons really useful. They usually make clear how the choice of language or library affects the code we write.

The Modular Monolith: Rails Architecture

In this post Dan Manges details how the engineering team at Root dealt with the fabled Rails monolith and made it more modular. The achieved a healthier separation of concerns, faster builds and got rid of circular dependencies by using Rails Engines to separate the different domains in their app. They obtained a lot of the benefits often attributed to micro-services, without adding layers of network traffic (and the failure modes that come with that) in the middle of their app.

Structs With Keyword Arguments in Ruby 2.5

Ruby 2.5 was released a few days ago. Among the new features, Structs gained the ability to be instantiated with using keyword arguments.

Ruby has traditionally had the ability to create a classes that bundle data attributes together, provide accessors for those attributes and other methods like converting into a hash:

1
2
3
4
5
Point = Struct.new(:x, :y)
p = Point.new(2, 3) # => #<struct Point x=2, y=3>
p.x # => 2
p.y # => 3
p.to_h # => {:x=>2, :y=>3}

Notice, that the newly created class is initialized with positional arguments. Often when using Ruby – especially when using Rails, data is passed around in hashes. For example, let’s assume that we are instantiating an instance of a Point inside a controller action using Rails. The instantiation would look something similar to:

1
point = Point.new(params[:x], params[:y])

As the number of positional arguments grow, this can become tedious. Ruby 2.5 ships with a new feature that allows creating Structs that accept keyword arguments, much like ActiveRecord models do, as described in this feature request.

1
2
3
Point = Struct.new(:x, :y, keyword_init: true)
Point.new(x: 1, y: 2) # => #<struct Point x=1, y=2>
Point.new(y: 2, x: 1) # => #<struct Point x=1, y=2>

There are a few things to note. When using keyword arguments, if a value is missing, it will be set to nil. Additionally, if extra arguments are supplied, an ArgumentError will be raised:

1
2
3
Point = Struct.new(:x, :y, keyword_init: true)
Point.new(y: 2) # => #<struct Point x=nil, y=2>
Point.new(x: 1, y: 2, z: 3) # => ArgumentError: unknown keywords: z

Stuck in an older ruby? You can easily build similar support on your own, which I often do in projects I work on:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module StructKeywordInitialization
  def initialize(args)
    members.each do |field|
      self.public_send("#{field}=", args[field])
    end
  end
end

Point = Struct.new(:x, :y) do
  include StructKeywordInitialization
end

Point.new(x: 1, y: 2)       # => #<struct Point x=1, y=2>
Point.new(y: 2)             # => #<struct Point x=nil, y=2>
Point.new(x: 1, y: 2, z: 3) # => #<struct Point x=1, y=2>

We’ve created a new module that takes advantage of the #members method in Struct to define a dynamic initializer. Note that in this version, extra arguments will not raise an ArgumentError. Depending on your application, this might be a better fit or not. It’s left to the reader to make a version that does raise an error with extra arguments.