-
The REPL: Issue 102 - Februrary 2023
That Wild Ask A Manager Story
This article references a story that is new to me. In short, the person that went through the series of interviews is not the same person that shows up for work. Instead of over-reacting, Jacob Kaplan-Moss argues that we should do nothing:
The premise here is simple: designing a human process around pathological cases leads to processes that are themselves pathological.
Postgres DDL Statements and Availability
This is a great reference of how each schema changing operation affects availability in Postgres.
A career ending mistake
John Arundel talks about career paths in software engineering:
As software engineers, we’re constantly making detailed, elaborate plans for computers to execute. Isn’t it weird that we rarely give a moment’s thought to the program for our own careers?
-
Using bashly to create a CLI
bashly is a command-line application that let’s you generate feature-rich command line tools. The idea is that you specify via a YAML file what subcommands, arguments, flags and environment variables you want for your executable, and
bashly
takes care of generating all the boilerplate on a bash script, so that you can focus on your code. Many languages support similar via libraries, like optparse in ruby.I recently used it to port a series of scripts for personal use that where all part of a series of commands I use to manage my personal note taking. I turned the all those separate scripts into a CLI with subcommands. Instead of
zk_title
andzk_today
, I know havezk title
andzk today
, among others).Here are my observations:
- The documentation is well done. In particular the examples showed me how to do everything I needed.
- The ability to check for required environment variables was very useful. If only a particular command requires a certain environment variable, that can be configured too.
- Reading from
stdin
or from a file is a very common use case. It’s well supported. - Commands can be aliased to shorter names.
- Flag handling is great. Short flags can be combined (i.e.
zk title -ps
instead ofzk title -p -s
) - Each command lives in it’s own file. If needed, custom functions that are called from other commands are supported.
- Some of my previous commands were written in Ruby.
bashly
supports heredocs, which make it possible to continue using ruby for portions of your script, albeit this is a bit of a hack and makes the script less portable:
/usr/bin/env ruby - ${arguments} <<-RUBY puts "hello #{ARGV}" RUBY
Note that for heredocs to work, the following environment variable needs to be set
BASHLY_TAB_INDENT=1
.Overall, I was happy with the results. All the boilerplate code like creating global and command
--help
output, argument and environment variable checking, and flag handling was abstracted away. -
The REPL: Issue 101 - January 2023
CTEs as lookup tables
Short and sweet. The syntax is nicer to read, and in my mind it fits better with the SQL mental model of relations.
Ransacking your password reset tokens
The ransack gem is a popular ruby gem to add searching capabilities to a Rails application. This article describes, compellingly, how ransack by default is open to exploitation and can be used to reveal sensitive information in an application. This process reminds me about how Rails allowed (insecurely) mass-assignment of params, which later was changed to not allow any params, unless specifically permitted. That approach is possible with Ransack, too. For existing applications, it can lead to a lot of allow-listing.
Anti-Pattern: Iteratively Building a Collection
It resonates with me that iteratively building an array feels wrong. But why?
The author states:
What follows are some lengthy method definitions followed by rewrites that are not only more concise but also more clear in their intentions.
So… is clarity the key?
Brevity and clarity are great, but one of the things that motivates me to use functional approaches over iterations is to minimize mutation. Written in a functional style your code handles less mutation of data structures, which means that it handles less state. Handling state is were a lot of complexity hides, and the source of bugs. According to Joe Amstrong, creator of Erlang:
Mutable state is the root of all evil.
-
The REPL: Issue 100 - December 2022
Just Use Postgres for Everything
Complexity can be reduced by having less dependencies and systems. Postgres is a fantastic technology, and getting better with every release. I’ve been doing what this article advocates for years: Using Postgres by default (e.g. JSON storage, back a job queue, full-text search), and only moving away when needed.
SQLite’s automatic indexes
Preetam Jinka explains how SQLite handles join on un-indexed fields: It creates a temporary index! This saves postgres from having to implement hash joins.
What I learned from pairing by default
Eve Ragins talks about what he learned when pairing by default. I’ve done a fair amount of pairing, but my sweet spot is no more than 2 or 3 hours a day. After that it becomes to tiresome. There is some exploratory work that I also rather do by myself, to avoid having to talk through everything I am thinking.
-
The REPL: Issue 99 - November 2022
Postgres: Safely renaming a table with no downtime using updatable views
Once again, Brandur posts a practical example of using Postgres effectively. The article covers how to rename a table safely using views. Other renames can be a bit more complicated, for example in that example, a table was renamed from
chainweel
tosprocket
. In a typical app, there will also be foreign keys pointing to the table, namedchainweel_id
(or similar). Those would still need to be renamed tosprocket_id
. Postgres includes support for generated columns:A generated column is a special column that is always computed from other columns. Thus, it is for columns what a view is for tables.
but it doesn’t quite have all the functionality needed to be able to change a column name without down time.
Vanilla Rails is plenty
Jorge Manrubia, from 37 Signals, objects to criticism that Rails encourages poor separation of concerns. Among the things that I agree with, is that the use of plain Ruby objects (POROs) is probably underused in most application. I don’t like some of the prescriptions in the article, though.
I don’t like concerns. While it’s nice that functionality is split into it’s own file, when included in models they end up making the API of then
ActiveRecord
model bigger. It’s already huge to start with. With large code bases, it can be very challenging knowing all the ways that ActiveRecord objects are being used. Adding more domain methods doesn’t make it better. Instead, I’ve had better luck using service objects. They make the APIs narrower. A win in my book.In the last few years, I’ve found that separating data from functionality is one of the patterns that gives great results and scales well. Value or data objects encapsulate the data. Other classes manipulate that data. Each has it’s own lifecycle. Mixing them together is the OOO way – which Rails leans heavily on – but it tends to create very broad interfaces (see
ActiveRecord
).