Type tinkering

I’m playing with typeful language stuff. Having only done a pinch of Haskell, Scala, and Go tinkering amidst Ruby work over the past ten years, it’s jarring. But, things are much better than they were before I started with Ruby.

Elm in particular is like working with a teammate who is helpful but far more detail oriented than myself. It lets me know when I missed something. It points out cases I overlooked. It’s good software.

I’ve done less with Flow, but I like the idea of incrementally adding types to JavaScript. The type system is pragmatic and makes it easy to introduce types to a program as time and gumption permit. Having a repository of type definitions for popular libraries is a great boon too.

I’m also tinkering with Elixir, which is not really a typed thing. Erlang’s dialyzer is similar in concept to Flow, but different in implementation. Both allow gradually introducing types to systems.

I’m more interested in types stuff for frontends than backends. I want some assurance, in the wild world of browsers and devices, that my systems are soundly structured. Types buy me that. Backends, I feel, benefit from a little more leeway, and are often faster to deploy quick fixes to, such that I can get away without the full rigor of types.

Either way, I’m jazzed about today’s tools that help me think better as I build software.

Universes from which to source test names

A silly bit of friction in writing good tests is coming up with consistent, distinctive names for the models or object you may create. Libraries that generate fake names, like Faker, are fun for this, but they don’t produce consistent results. Thus I end up thinking too hard.

Instead, I like to use names from various fictional-ish universes:

Hopefully my teammates enjoy these little easter eggs as much as I enjoy looking them up when I need something fancier and less dry than metasyntactic variables.

You should practice preparatory refactoring

When your project reaches midlife and tasks start taking noticeably longer, that’s the time to refactor. Not to radically decouple your software or erect onerous boundaries. Refactor to prepare the code for the next feature you’re going to build. Ron Jeffries, Refactoring — Not on the backlog!

Simples! We take the next feature that we are asked to build, and instead of detouring around all the weeds and bushes, we take the time to clear a path through some of them. Maybe we detour around others. We improve the code where we work, and ignore the code where we don’t have to work. We get a nice clean path for some of our work. Odds are, we’ll visit this place again: that’s how software development works.

Check out his drawings, telling the story of a project evolving from a clear lawn to one overwhelmed with brush. Once your project is overwhelmed with code slowing you down, don’t burn it down. Jeffries says we should instead use whatever work is next to do enabling refactorings to make the project work happens.

Locality is such a strong force in software. What I’m changing this week I will probably change next week too. Thus, it’s likely that refactoring will help the next bit of project work. Repeat several times and a new golden path emerges through your software.

Don’t reach for a new master plan when the effort to change your software goes up. Pave the cow paths through whatever work you’re doing!

Let’s not refer to Ruby classes by string

I am basically OK with the tradeoffs involved in using autoloading in Rails. I do, however, rankle a little bit at this bit of advice in the Rails guide to developing engines.

Screencapture from Rails Guide to Engines
Screencapture from Rails Guide to Engines

In short, when configuring an engine from an initializer, you’re supposed to protect references to autoloaded application classes (e.g. models) by encoding them as strings. Your engine later constantizes the string to a class and everything is back to normal.

A thing I am not OK with is “programming with strings”. As an ideal, strings are inputs from other machines or humans and internal references to code are something else. In Ruby, symbols fill in nicely for the latter. Could I refer to classes as Symbols instead of Strings and live with the tradeoffs?

Well it turns out, oddly enough, that Rails is pretty sparing with Symbol extensions. It has only 120 methods after Rails loads, compared to 257 for String. There are no specific extensions to symbol, particularly for class-ifying them. Even worse (for my purposes), there isn’t a particularly great way to use symbols to refer to namespaced classes (e.g. Foo::Bar).

But, the Rails router has a shorthand for referring to namespaced classes and methods, e.g. foo/bar#baz. It doesn’t bother me at all.

In code I have to work with, if at all possible, I’d rather refer to classes like so:

  1. Refer to classes by their real ClassName whenever possible given the tradeoffs of autoloading
  2. When autoloading gets in the way, refer to things by symbols if at all possible
  3. If symbols aren’t expressive enough, use a shorthand encoded in a string, e.g. foo/bar#baz
  4. … (alternatives I haven’t thought of yet)
  5. Refer to classes by their full string-y name

But, as ever, tradeoffs.

Your product manager could save your day

I thought the feature I’m working on was sunk. An API we integrate with is, let us say kindly, Very Much Not Great. Other vendors provide an API where we can request All The Things and retrieve it page by page. This API was not nearly so great, barely documented, and the example query to do what I needed didn’t even work.


Luckily, only an hour in, I rolled over to the product manager and asked if it was okay if we were a little clever about the feature. We couldn’t request All The Things, but we could request Each of The Things that we knew about. It wasn’t great, but it was better than sunk.

The product manager proceeded to tell me that was okay and in fact that’s kind of how the feature works for other APIs too. I hadn’t noticed this because I was up to my neck in code details. She described how this feature is used in out onboarding process. It wouldn’t matter, at that level, whether we made a dozen requests or a hundred.

I wrote basically no code that day. Someone who “crushes code” or “moves fast and breaks things” would say I didn’t pull my weight. Screw ’em.

I didn’t go down a rabbit hole valiantly trying to figure out how to make a sub-par API work better. I didn’t invent some other way for this feature to work. My wheels were spinning, but only for a moment.

Instead, I worked with the team, learned about the product, brainstormed, and figured out a good way forward. I call it a very productive day.

The occurrence and challenge of ActiveRecord lookup tables

I’ve noticed lots of Rails apps end up with database-backed lookup tables. Particularly in systems with some kind of customer or subscription management, it’s almost guaranteed that User or Customer models belong_to SubscriptionLevel or Plan models. Thus, you frequently need to query both models.

If you’re looking for avoidable database work, as I sometimes have, this seems like low-hanging fruit. Plan level models very rarely change. You could replace those Plan or SubscriptionLevel models with a hardcoded data object and move on.

In my experience, you now have a white whale on your hands. This low-hanging fruit may haunt you for a while. It could cause you to invent increasingly implausible mechanisms for ridding yourself of this “technical debt” (scare quotes, it’s a trade-off and not actual technical debt). Teammates will appear drowsy when you mention this problem and its technical details, then back away slowly.

Why is it so tricky to convert AR database lookups to non-AR in-memory lookups?

I’ve attempted this twice. Both times, I tried to grab as little surface area as possible and ended up with nearly all of the models. A current teammate is trying now and suffering a somewhat similar fate. They’re more detail-oriented and motivated than I am, so I hope they’ll succeed. (Ed. they succeeded!)

Is this phenomenon something we can easily write off to coupling or is it something else? My pessimistic, gossip-y sense leads me to think people who have become ORM Skeptic went down this path thinking it’s inevitable if you accept an ORM into your life. They came away a dark shade of who they were with the conclusion that ORMs ruin everything. However, the phases of coping that involve a three thousand word essay and then writing a new database layer thing don’t actually solve this problem.

In the ActiveRecord flavor of ORMs, it is easy to describe model graphs and interactions amongst those graphs. Once you’ve got the whole model graph, its often difficult to isolate a subsection of it. AR, in particular, can make it easy to violate Demeter and reach through that graph in hard-to-refactor ways.

Our lack of great and general tools for working with Ruby code that uses these graphs and rewriting said code is another big challenge. Solving these problems requires visualizing the direct and transitive connections between models. Then you need some kind of refactoring tool to rewrite code to use an indirection object instead of directly coupling. We lack both of those in the Ruby world.

Optimistically, I’d think this is a case of refactoring smarter. Given a solid test suite you could:

  • connect your lookup models to an in-memory SQLite database populated at app start, no need to remove ActiveRecord
  • use one of the several libraries that implement enough of the ActiveRecord interface to replace models with classes backed by static data
  • lots of things I haven’t thought or heard of!

The thing you wouldn’t want to do, and where I faltered at least once, was to let it become a long-running task. When you’re making changes all over the code base, any amount of churn behind your back is potentially crippling. If you can freeze the code base, I highly recommend it. (Coincidentally, this is exactly what my smarter-than-me teammate did!)

The other thing to keep in mind is that, inevitably, you will come across weird uses of ActiveRecord and Rails that you didn’t know about, are pretty sure you don’t like, and have to work with anyway. Set aside time for these known unknowns.

When dealing with potentially large, radical changes to your application code, how radical are you willing to go to make many smaller changes than one big one? There’s no crisp answer here. All code grows awkward in different ways. As always: divide, conquer, and celebrate your victories!

The right way and the practical way

Brent Simmons, Reason Number 33,483 to Hate Programming:

Or I could have the superclass expose the appIsTerminating property in its header file, so that the subclass could see it. This also sucks, because a controller class has no business exposing its own copy of global application state.

In the end, though, that’s what I did. (Along with a comment that the property was there for subclasses.)

It reminds me that there are two competing values:

  1. Do everything the right way every time.

  2. Make responsible and professional decisions about time and expenses and benefits and drawbacks.

My nature is to take path #1. It is so hard for me to take path #2. I have the utmost respect who can work on sprawling, modern software and stay on path #2. But path #1, always pulling me in and sending me down rabbit holes.

Sometimes I wonder which of these paths got me to where I am in my career. Others, I wonder if I think I’m a everything-the-right-way person but really I’m a responsible-and-professional-tradeoffs person.

A brain’s a weird place to live.

Framework and Library people

By unscientific survey, I think many developers would prefer to work in a “framework world” where many decisions of principle and organization are passed down by a vendor or architecture team. Think Rails/Django/Laravel for backends, Ember/Elm for frontends, Unity for games. These are the Framework people.

Fewer developers would prefer to create their own world, building up tools and libraries to suit. They select a few first principles and build their own world. They’re the bebop jazz musician, eschewing big band gigs and music people can dance to to create their own intellectual world. These are the Library people.

I’m a Framework person. The allure of Library people sometimes tempts me after I look at a beautifully-restored car or a well-structured song. But constructing a library world is thankless and not particularly high leverage, unless you succeed in creating something for framework people. Weird, eh?

Empathy Required

Nearly fourteen years ago, I graduated college and found my first full-time, non-apprentice-y job writing code. When I wrote code, these were the sorts of things I worried about:

  • Where is the code I should change?
  • Is this the right change?
  • What are the database tables I need to manipulate?
  • Who should I talk to before I put this code in production?

Today, I know a lot more things. I did some things right and a lot of things wrong. Now when I write code, these are the sorts of things I worry about:

  • Am I backing myself into a corner by writing this?
  • Why was the code I’m looking at written this way and what strategy should I use to change it?
  • Will this code I just wrote be easy to understand and modify the next time I see it? When a teammate sees it?
  • Should I try to improve this code’s design or performance more, or ship it?

Half of those concerns are about empathy. They’re only a sampling of all the things I’ve learned I should care about as I write code, but I think the ratio holds up. As I get better and better at programming, as my career proceeds, I need more empathy towards my future self and my teammates.

Further, that empathy needs to extend towards those who are less experienced or haven’t learned the precise things I’ve learned. What works for me, the solutions that are obvious to me, the problems to steer clear of, none of that is in someone else’s head. I can’t give them a book, wait three weeks, and expect them to share my strengths and wisdoms.

That means, when I advise those who listen or steer a team that allows me to steer it, I have to make two camps happy. On one hand, I have to make a decision that is true to what I think is important and prudent. On the other hand, I have to lay out guidelines that lead the listener or teammate towards what I think is important or prudent without micromanagement, strict rules, and other forms of negative reinforcement.

It’s so easy, for me, to just hope that everyone is like me and work under that assumption. But it’s much better, and highly worthwhile, to figure out how to help friends and teammates to level up on their own. It requires a whole lot of empathy, and the discipline to use it instead of impatience. Worth it.

Copypasta, you’re the worst pasta

Copypasta. It’s the worst. “I need something like this code here, I’ll just drop it over there where I need it. Maybe change a few things.” Only you can prevent headdesks!

It’s not really possible, in my experience, to make it easier to use code through methods and functions than to just copy what you need and start changing it. No amount of encapsulation or patterns is easier than a pasteboard.

Perhaps, copypasta’s natural predator is a well-informed code review. There are tools, like flay, that can detect some kinds of code duplication.

But for the most part, it’s a battle of dilligence.

(Ed. I found this in my draft folder from four years ago. Copypasta; copypasta never changes.)