-
The REPL: Issue 75 - November 2020
Why Ruby Class Methods Resist Refactoring
Sasha Rezvina explores why class methods in Ruby are hard to refactor. They tend to accumulate lots of logic. I prefer limiting class methods to “builder” methods that instantiate new objects, like
.initialize
.Verbal Expressions: Ruby Regular Expressions made easy
This looks like a fantastic library. It provides a way to “verbally” construct regular expressions:
tester = VerEx.new do start_of_line find 'http' maybe 's' find '://' maybe 'www.' anything_but ' ' end_of_line end tester =~ "https://www.google.com"
It supports string replacement, and capture groups too!
Moving my serverless project to Ruby on Rails
This article illustrates one of my worries about the server-less trend: Each lambda function might be simple, but there interactions are not, and that is hard to reason about (and deploy!).
When the building blocks are too simple, the complexity moves into the interaction between the blocks.
-
The REPL: Issue 74 - October 2020
Introduction to the Zettelkasten Method
The Zettelkasten method is a sort of personal note-taking method. This articles explains why the method is interesting in the first place, it’s principles and a few possible ways of using it. I’ve been gravitating in the last few years to a similar method, which doesn’t quite adhere to all the principles. I’ve long known that writing things down helps clarify your thoughts; having to articulate your thoughts, makes you a better thinker. It also helps with knowledge retention. What struck me most as I read this article is that there is another level of value that comes from the effect of capturing more things and linking thoughts together. That is what turns facts into knowledge: Knowing how it relates to other thoughts.
As I was reading, I noticed that I often capture thoughts in my daily note. That is better than not capturing at all, but it doesn’t make it discoverable in the future. Since reading the article, I’ve started creating separate notes for each individual “thought” and tagging it. I am also making an effort to link – at the time of writing – with other notes.
The Pyramid Principle
Shiva Prabhakaran at Models HQ writes about the Pyramid Principle, which recommends that you start communicating with your answer/hypothesis first and then support it with arguments and data.
My natural instinct when speaking, is to do something similar to this recommendation (but not quite): Start with the answer, and then add context. This recommendation goes a bit further: Answer, summary, and then supporting arguments.
When writing, my instinct is more to start from the context, walk through the rationale and arguments, and then get to the conclusion. That follows my train of thought, which I expect my readers to follow. This article explains why it might be better to start in the other direction.
The recommendation comes from consulting companies and their communication with executives. Its applicability might be limited.
Rbspy
This week I found myself working on optimizing the performance of a refactored Rails endpoint. I found
rbspy
:rbspy lets you profile Ruby processes that are already running. You give it a PID, and it starts profiling. It’s a sampling profiler, which means it’s low overhead and safe to run in production.
rbspy lets you record profiling data, save the raw profiling data to disk, and then analyze it in a variety of different ways later on.
This made it painless to get data on a running process without much fuzz. It even generates flamegraphs.
-
The REPL: Issue 73 - September 2020
Under Deconstruction: The State of Shopify’s Monolith
The engineering team at Shopify discuss the state of their Rails monolith, how it has evolved over time, the lessons they’ve learned and what is in store for the future. Most of the information is relevant for Rails developers working in large systems, with large teams.
As part of their efforts to make their monolith more effective, they are introducing a newly-open sourced tool: Packwerk. The objective is to enforce modularity, through the use of static analysis.
Writing a book: is it worth it?
Martin Kleppmann discusses openly the economics of writing his book: Designing Data-Intensive Applications. The book has been one of my favorite technical books. The book has generated almost $500,000. It involved working on it for years – one of them without any other income, speaking at more than 50 conferences promoting the book, and was helped in no small part because of Kleppmann’s well deserved presence and reputation in the field.
-
The REPL: Issue 72 - August 2020
Error handling with Monads in Ruby
Vitaly Pushkar goes over error handling in programming languages and the reasoning for using monads for error handling. I’ve been experimenting with that at work record with very positive results – albeit in a limited portion of our code.
There is a section where the author talks about the “pyramid of doom” – much like the dry-monads documentation. The “do notation” is presented as a solution. Maybe it’s my lack of familiarity with that notation, but I think that there is a simpler solution, which I call “railway oriented”.
Pyramid of Doom:
def call(fields) validate_fields(fields).bind do |fields| validate_email(fields['email']).bind do |email| find_user(fields['id']).bind do |user| update_user(user, {name: fields['name'], email: fields['email']}).bind do |user| send_email(user, :profile_updated).bind do |sent| Success(user) end end end end end end
Do notation:
def call(fields) fields = yield validate_fields(fields) email = yield validate_email(fields['email']) user = yield find_user(fields['id']) user = yield update_user(user, {name: fields['name'], email: fields['email']}) sent = yield send_email(user, :profile_updated) Success(user) end
Note that the
yield
in the above solution differs from the standard Ruby meaning. It doesn’t yield to the block passed to#call
but rather binds the Result and halts execution of the method on failures.My “railway oriented” solution:
def call(fields) validate_fields(fields). bind { |fields| validate_email(fields['email']) }. bind { |_email| find_user(fields['id']) }. bind { |user| update_user(user, {name: fields['name'], email: fields['email']}) }. bind { |user| send_email(user, :profile_updated) } end
The “pyramid of doom” is avoided, without having to change the Ruby semantics.
How to stop procrastinating by using the Fogg Behavior Model
The Fogg Behavior Model is a new concept for me. It presents a mental model in which:
Behaviour = Motivation + Ability + Trigger
A lack in any of the three factors can prevent behavior from occurring. The articles then talks about different strategies to boost each factor. For example, blocking time in your calendar can serve as a trigger to start working on a particular task. I’ve been very successful with that technique lately.
-
The REPL: Issue 71 - July 2020
Simple Made Easy
This talk is not new, but it is new to me. I’ve seen reference to this talk in many places, but hadn’t watched until recently.
Rich Hickey emphasizes simplicity’s virtues over easiness’, showing that while many choose easiness they may end up with complexity, and the better way is to choose easiness along the simplicity path.
This is a fantastic talk! It describes how software that is simple is the ultimate goal. Simple is achievable, but it is not the same as easy. It goes hand-in-hand with choosing the correct abstractions and planning beforehand. It’s not about hiding complexity, it’s about abstracting it away. It reminds me of something that I distilled from somewhere else as:
An abstraction removes from your mental load. Indirection adds to it.
An abstraction lets you forget about the details and allows higher order thinking. Indirection forces you to think about the details constantly.
Result Objects - Errors Without Exceptions
Tom Dalling of Ruby Pigeon introduces his library Resonad. It aims at dealing with error handling without exceptions. The article gives a good overview why and when to use result objects. There are other libraries in the Ruby ecosystem (like dry-monads). As the author points out, it doesn’t actually take a lot to build your own. I did so recently and was pleasantly surprised how far a very simple implementation can take you:
class Failure attr_reader :error def initialize(error) @error = error end def successful? false end def failure? true end def and_then self end end class Success attr_reader :value def initialize(value) @value = value end def successful? true end def failure? false end def and_then yield(value) end end Success. new(1). and_then { |v| Success.new(1 + 1) }. and_then { |v| Failure.new(:boom) }. and_then { |v| Success.new(1 + 1) } # => #<Failure:0x00007f9eda02f280 @error=:boom>
At work, I am evaluating the usage of result monads as part of some work to better encapsulate domain logic. I am expecting it will be very beneficial in how we handle errors.