A tale of two Rails views

At sign

Why do I prefer to avoid referencing instance variables in view?

I needed to clarify this personal principle to a teammate recently. It was one of those things I’ve internalized so strongly that it was initially difficult for me to do so; one of those things that have become so true to me that teaching someone else about it requires getting past the “well, obviously it’s the best, that’s why” phase of mental tantrum.

An entirely made-up, possibly oversimplified Rails view fragment:

<p>Hi, my name is <%= @user.name %></p>

This is a by-the-book Rails view. Set an ivar in an action, use it in views (and helpers), you’re done, get back to thinking about building product.

It’s easy to iterate with this. It’s easy to see data “passed”1 into the view by scanning for the @ sigil; your editor probably does this already. It’s easy to move markup around in templates with a simple cut-paste.

If you stick with ivars long enough, you’re going to end up with two kinds of misadventures.

Most commonly, you’ll wonder why @user is nil for some view or edge case. Maybe you forgot a filter or didn’t set it in an action? Backend developers are sometimes equipped with the curiosity and knowledge to fix this themselves. For front-end developers or those new to the system, this kind of error is basically “womp-womp sad music go interrupt someone who can help you”.

This leads to the second misadventure: where did this @user thing come from2? Maybe it was set in a helper, or a filter, or an action? Well now you’ve painted yourself into a weak spot of a language like Ruby. Your tools probably can’t point you directly at the line of code where a variable came into being. You can do some clever grep’ing around3, probably. At best, you know the system well enough to get to the right file and find it, or there’s some convention you can use to intuit where it might be set.

Of course this way is better, right?

<p>Hi, my name is <%= current_user.name %></p>

Well, not initially. Already you have to pick a good name for a method because you’re probably going to use it all over the place. Then you have to find a good place to put that method: on a helper method? on a helper object? on a model?, i.e. now you’re making decisions, which is a thing Rails tries to shield you from wherever possible. Personally, I find naming things quite entertaining and hardly one of the hardest problems in computer science4.

A marginal benefit comes from reducing the entry points that the name current_user came to be a thing in this view: it’s a method name, a local variable, or a view-local variable passed into the template5. Thus the search space for “where did this thing come from” is way smaller for typical Rails templates and manageably smaller for unreasonable Rails templates.

This way pays off once the application gets past the point where new code tidily fits into models, views, controllers, or helpers. At that point, you need objects, messages, and experience at building with objects and messages (successes, stalemates, and abject failures)6. If you’re competent at messages (i.e. method calls) in Ruby, you can at this point experience a “this is Unix, I know this!”7 moment and work with this method call like you would any other method/function invocation in a computer program.

Making this investment into a method call yields another long-term benefit: simplicity in experimentation and testing8. I can poke at helper methods and helper objects in a Rails console without breaking a sweat. If that feedback loop doesn’t get the job done, I can write tests against helpers and (some) controller methods to iterate on figuring out why something doesn’t work the way I think it should.

It’s amazing that blogging about programming is so popular. Programming involves a lot of tradeoffs. Tradeoffs make for wordy articles that don’t leave you thinking “yeah, those other guys are SOOOO WRONG” or “YEAHHHH I’m so right”.

If I’ve written this properly, hopefully you felt both of those feelings. Maybe you reminisced about a day when you thought view ivars were great and then regretted it. Perhaps it’s easier to see why you’d start an app or feature off using ivars and then refactor them to method calls later.

With instance variables in views, as with many other grey areas of Rails, the most useful long view is this: you’re always right, and you’re always wrong, it just depends on which “you” (past you, present you, legacy project you, greenfield project you) is being observed.

  1. I’m not using “passed” as scare quotes here. Rails’ choice to take ivars from actions and teleport them into views is oft villified, but I find it more useful to think of them as something that just is. They are incredibly useful at first, but some developers will long for an explicit contract (i.e. a parameter list) between action and view. 
  2. If you’re an object design enthusiast, functional programming aficionado, or Rails contrarian you may be crafting an amazing retort about sharing state in weird ways at this point. Please review the previous footnote. Yes, you’re basically right. No, that’s not going to change how Rails works. 
  3. Regexes, two problems, etc. 
  4. Actual hard problems in computer science: systems connected by unreliable (i.e. any) network, laws and regulations written by humans, working with money. 
  5. When it comes to variables in Rails templates, a short guide by Adam Keys; method names: friend, ivars: foe, local variables: foe, view-local variables (passed via render explicitly or by the framework): both! 
  6. This is the concise version of a seems-really-important-to-me idea I need to express in more words. Remind me to write this, should I forget about it, internet! 
  7. What up, Ariana Richards
  8. You knew the testing thing was coming, didn’t you? I mean it’s like Chekov said about guns in stories: if method calls are mentioned in the first act, you have to mention TDD in the last act. If you headdesk’d, consider that you might be annoyed by TDD because you keep giving yourself a head injury when it’s mentioned. 

Grow and cultivate

Adding new functionality to software is really exciting. I love poking around the edges of a system, figuring out what’s going on, and looking for that “obvious” place where the first change should happen. But sometimes, it’s hard to know if I’m making the right change. How Should This Work?

The temptation when changing an existing system is to implement the desired behavior within the structure of the current abstractions. Repeat this without adjustment, and you’ll quickly end up contorting existing concepts or working around legacy behaviors. Conditionals pile up, and shotgun surgery becomes standard operating procedure.

Even if you code test first, you can make a mess of a system. What you end up with is a system that moves from local maximum to local maximum while the time of the test suite grows unbounded. There are worse things that could happen, but no one’s going to jot this one down as a “best practice” either.

The counterforce to this temptation is the red-green-refactor cycle. Look at how the system works and figure out how the next bit of work might change how the system works. Refactor to simplify the act of making a change, or make the change and refactor afterwards to a better design.

Software can grow by accretion, but it stays malleable when the team culture is one that balances continuous growth with continuous cultivation of good design.

Not that kind of log

Logs from Finland

First, read all of this excellent distillation of distributed systems by Jay Kreps,  The Log: What every software engineer should know about real-time data’s unifying abstraction. Now, consider this.

There’s a moment, when you’re building services and web APIs, when you think you’ve pretty much got it under control. You’ve got an endpoint for every query, a resource for every workflow. All the use-cases seem to be under control. And then, the question appears:

“How can I get access to all the updates to all the data? You know, for [REASONS].”

For APIs exposed to external developers over the web, this is where you’d reach for web hooks or PubSubHubbub. It’s not the best solution, but it works. If you’re building an internal system, you could  use the same approaches, or…you could build a log.

No not that kind of log. An event log, like LinkedIn did with Kafka for their internal systems. Every time your data model changes, every create, update, or delete, you drop an event with all the metadata related to the change. The event goes into some kind of single-producer, multiple consumer queue. Then all the clients that want to know about all the changes to all the things can read events off the queue and do whatever it is they need to for those important REASONS.

If you find this intriguing, this is a lot like replication in database systems. Definitely read LinkedIn’s article on this, and definitely read up on how your database of choice handles replication. And if you’ve built this before and have a good answer to initially populating “replicas” of a database, let me know; I haven’t come up with anything better than “just rsync it”.

Dear Sync Diary

Synchronize icon

Brent Simmons is keeping a diary as he works through implementing sync for Vesper, an iOS note-taking app. Building this sort of thing isn’t easy; cf. it took Cultured Code multiple years to implement it for Things. Thus it’s pretty neat that Simmons is breaking it down into understandable chunks:

If you think you need to implement a synchronization system for your application, try to find a shortcut so that you don’t. If you can’t find a shortcut, you could do worse than starting with these notes.

Aliens ate my program’s state

Cute aliens

So, Adam from a few years ago, you think you can build a distributed system?

Designing a concurrent system, one that runs across multiple processors using shared memory threads, is difficult because someone can move your cheese. One moment you’ve got food on your plate, and the next moment it’s on someone else’s plate.

Designing distributed systems, one that runs across multiple computers over networks that occasionally do weird things, is difficult because you can’t assume that the message will get through. One moment you’re telling your friend about an awesome new album and the next moment they don’t even know who you are anymore.

In both scenarios, you can’t assume the simplest case. You have to discard Occam’s razor; it’s entirely possible a perfectly devious alien is tinkering with your system, swallowing messages or preventing threads from running. For every operation in your system, you have to ask yourself what could go wrong and how will my system deal with that?

The first jarring thing about these sorts of problems is that you’re probably not using a framework. There are libraries to help you with logging, instrumentation, storage, coordination, and low-level primitives. There aren’t a lot of well-curated collections of opinions expressed as code. There’s no Rails, no Play, no Django, no Cocoa. There’s hardly even a Sinatra, a Celery, a JDBC.

That means you’re going to be designing, for real. Drawing on whiteboard, building prototypes and proofs of concepts. Getting feedback from as many smart people as you can corner. Questioning your assumptions, reading everything you can find on the topic. Looking for tradeoffs that decrease risk and simplify your problem domain.

Accept that you’re unlikely to ever get it entirely right. Those who are really good at it are more like Scotty than Spock. They’re surrounded by levers and measurements, tweaking and rebuilding the system as they go. It’s a fun puzzle!

Database rivalry in the Valley

A couple years ago, Google released an embeddedable key-value database called LevelDB. There was much rejoicing. Recently, Facebook released their fork of LevelDB, RocksDB. There’s a slide deck comparing the two, but the amusing part is the “our way is better!” subtext. A little bit of Valley rivalry there. You can also learn a lot of interesting tidbits about the design of modern, high-performance database systems from the Architecture Guide and Table Format documentation of RocksDB.

Say what you will about tiny, highly-targeted ads. To some extent they are subsidizing a lot of interesting technology development and the open sourcing thereof.

Object-oriented relativism

When a Method Can Do Nothing, Michael Feathers:

If polymorphism means anything at all, it means that the object is in charge. We send it a message and it is up to it to decide what to do. That’s core to OO and part of Alan Kay’s original view of objects – that they are all about messaging. That said, it is not the dominant view today.

The majority of this article is on working with/around conditionals using intention-revealing method names or null objects. Yet, this paragraph smacked me in the face with “oh, yeah, obviously!”. Lots of people view the moving parts in object-oriented languages as ways to group and share functionality. But to people who talk about OO a lot, read the history books, read the pattern books, know what SOLID is, etc. it’s an entirely different thing.

Here’s a sports metaphor: the Dallas Cowboys are a widely disliked sports team, for various reasons. If I was from anywhere but Dallas, it would be “not cool” to count myself one a fan. But being from Dallas, I have an entirely different view on the Cowboys and can safely watch their mostly mediocre performances with occasional memories of greatness, safe from scorn.

I find that holding that tension in my head is important when talking to sports fans. It’s the same with OO: you haven’t read the books, watched the presentations, or worked the exercises I have. We’re on different pages, but we need to talk about the same code and how to structure it. There’s a tension between my understanding of OO and the next person, but it’s not a barrier. We have to get our language straight before we can talk about language!

In short, we have to establish what city in OO-land we’re from before we can effectively talk about OO.

Breakfast cannot wait, Prince

Look, Prince, I’m not sure if I can agree with you regarding “Breakfast Can Wait”. Cereal? Toast? English Muffins? Sure, those things can wait. Migas? Pancakes? Bacon? These things cannot wait. And don’t get me started on eggs; wait too long and delicious eggs can go rubbery and gross all too quickly.