At any large conference like RailsConf, there’s bound to be a mix of presentations. There’s only so many talks you can listen to about service-oriented architecture (or TDD?) before it becomes a catchphrase, devoid of all practical meaning. Thankfully, Ruby Central, the RailsConf organizers, tried to organize the talks around certain themes within four tracks in order to both group together and spread out the related talks. These themes had titles like “Beyond ERB” (how to set up front-end views without ERB), “Play Nicely” (how to work in a team environment and contribute to open-source) and “Big Rails” (how to manage and refactor a monolithic Rails app).
Overall, I found these groupings helpful for determining what I wanted to focus on and largely successful in guiding me toward the talks I thought most interesting. As much as I enjoy a good technical talk on introducing a services layer into my Rails architecture, less code-centric presentations like the one I attended on the serendipitous discovery of a sensible user interface for microwaves are just as enjoyable. Coding is just a part of the wide world of software development, so the fact that the organizers sprinkled talks on career development, design sensibilities and product management throughout the four days made the technical conference more palatable and enjoyable to me.
All The Little Things - Sandi Metz
Beyond the two keynotes I mentioned in my last post, Sandi Metz gave a fantastic presentation on refactoring - I recommend everyone reading this to go check it out when it comes online. She took a massively nested conditional (clocking in at around 50 lines of code) and systematically broke it down into small methods and then small classes with a well-defined API of limited scope, all the while using the existing test suite to guide her actions. This is what’s so useful about a robust test suite - you can refactor the underlying code without knowing anything about the problem domain, so long as the tests cover all the business-necessary functionality (which, granted, is a big IF).
Sandi turned me on to the Squint Test - a process of inferring complexity in your code from the shape and color of your classes and methods. The Squint Test basically recommends squinting at your screen, or otherwise turning down the visual clarity of your code, and discerning the shape and color of your code. A flat shape indicates less nesting compared to a lumpy shape, since if-statements and blocks cause indentation within a method. Similar colors indicate a similar level of abstraction, since text editors will color constants differently from local or instance variables. The rule of thumb this hits on is keeping your methods to a tight layer of abstraction - i.e., mostly of the same color - rather than having all colors of the abstraction rainbow in a single method. I love this test because its a visual heuristic - taking advantage of our natural recognition of colors, shapes and forms to indicate code complexity.
She also argued that keeping code DRY at all times should not be your first priority as a developer. Duplication is sometimes an easy monster to slay because it is both easy to spot and we’re taught that its bad from the very beginning of our coding journey. Yet duplication is preferable over the wrong abstraction (e.g., a massively complex conditional or large classes), and so the focus should first be on refactoring toward the right abstraction before cutting back on duplication. In fact, as you move toward the goal of simplicity, you’ll likely encounter more intermediate complexity and duplicated code along the way.
What I love about Sandi Metz is that even though she’s clearly a great developer, she is able to address the obstacles facing new or junior developers who may not have heard of half the concepts in many a RailsConf presentation. She speaks about code in a plain, accessible manner without the mumbo-jumbo of developer buzzwords like TDD or Domain-Driven Design. She takes everyday anti-patterns that developers face on a daily basis (who hasn’t encountered a multi-level nested conditional?) and systematically demonstrates both why those anti-patterns are harmful for the long-term development of an application and how to correct it.
Tricks That Rails Didn’t Tell You About - Carlos Antonio da Silva
Many talks at RailsConf described how to move from a monolithic Rails app to a service-oriented (or, even, hexagonal) architecture, or focused on how to design a better domain model. I enjoyed this talk by Carlos (developer at Platformatec and a member of the Rails core team) because it fell on the opposite side of the concrete-abstract spectrum, in that he spoke about numerous handy, practical tips in Rails that I was previously unaware of. Tips like the difference between
Array#exists? (the former loads the association in one query) and the
rake notes command to see a command-line summary of the comments and notes in your app. While I may not be able to move immediately to an SOA tomorrow, I can start using these tools in my day-to-day code.
Also, did you know that running
rake rails:update will initiate a guided tour of your app to update to the most recent version of Rails? I sure didn’t, but now I have a single source to figure out what I need to change when updating to the newest Rails version.
Effectively Testing Services - Neal Kemp
In his talk, Neal (independent developer) discussed several different strategies for testing services, both external APIs (e.g., Facebook) or internal services, like those I use at the NYT. As your software architecture becomes increasingly service-oriented, maintaining the touch points with other teams become all the more critical. Having a dependency on an inconsistent API can make it more challenging to identify bugs in your code, since it can create uncertainty as to whether your code is broken or the service you depend on is faulty.
At the NYT, we often interact with other teams through an API. We’ve found that the integration points where services interact are often high on the list of pain points, especially when those services are under active development and you may be interacting with a less-than-stable version of an internal service. Even beyond stability and scalability of a service maintained outside of your immediate group, services can have other, often more insidious, problems, like inconsistent return values.
For example, I’ve had the experience working on an internal API that I thought always returned an collection, unless there was only one object, at which point the response would be a hash of key-value pairs rather than an array of one. The most pernicious factor of these inconsistent return values is not that they occur, but that they are hidden and data-dependent, and are thus more prone to pop up in production when you least expect it.
Helpful tools like webmock, ShamRack and VCR allow you to stub out requests in your testing suite (e.g., webmock) and record / play back specific responses so that you aren’t dependent upon network connection when running your tests (e.g., VCR). We use these mocking tools in my group, and they’ve proven quite useful in testing our integration with various services, both internal and external (e.g., AWS).
Discovering User Interactions - Cameron Daigle
Approaching user interface design from a web development perspective, I’m often bemused at the fact that many objects we come in contact with on a daily basis have unnecessarily inscrutable interfaces. For example, I still - to this day - find the average TV remote control difficult to decipher at first glance, and if I hadn’t spent many years watching TV as a child, I probably would have little intuitive understanding of what to do after I press the “ON” button. As a developer, I have the power to change a web application’s user interface by sprinkling in some JS event handlers or CSS animations. But physical objects like a remote are built at a single point in time and have no dynamic ability (disregarding more modern, touchscreen controls) to change their buttons or switches.
Cameron (senior designer at Hashrocket) touched on these ideas in an insightful and humorous talk on how everyday objects like coffee makers and ATMs can provide guidance on how best to design a user interface. He made the argument for consistent interfaces that leverage commonly held patterns of behavior and understanding among the expected user group. Avoid using different “modes”, like how some microwaves confusingly have various modes for defrosting, cooking, softening, etc. Make use of low-cost shortcuts that minimize the need for a user to make a decision, like how certain coffeepots automatically turn the hot water spout over the ground coffee when the lid is closed. And don’t make assumptions about user behavior that could have disasterous consequences, like how a certain other coffeepot has a “self-destruct” button that swivels the pouring vessel away from the pot when pressed, even while brewing.
Intuitive user interfaces are great, but I sense that there is a tension between the simplicity of a user interface and the broad usability of that interface. Cameron argued that design should leverage your users’ intuitive understanding of interaction. But whose intuition? A simple, intuitive design implicity and by necessity builds off of some cultural assumptions about how people respond to commands and instructions. Everyone “knows” that red means stop and green means go, except when they don’t, so doesn’t an interface built with this in mind potentially exclude users that don’t have that cultural understanding? Granted, this is a contrived example, and clearly the goals of achieving interface clarity, deep functionality and broad usability are not entirely mutually exclusive. Nevertheless, its important to recognize the tradeoffs between these goals. There will always be a tension in priorities when trying to design the “best” user interface. What I think matters the most is being purposeful in your design choices and knowing what they cost.
Curmudgeon: An Opinionated Look at an Opinionated Framework - Ernie Miller
After all the talks by the heavy hitters in the Ruby/Rails community (e.g., DHH, tenderlove and Sandi), I thought this final-day presentation by Ernie on the drawbacks of Rails as a heavy framework quite insightful.
For example, run the following command via console in a bare-bones Rails app:
ActiveRecord::Base.ancestors, and you’ll see what Ernie was talking about regarding the heaviness of ActiveRecord in how many modules and classes are included when you inherit from ActiveRecord. The ORM simply tries to do a ton of stuff - timestamps, reflection, serialization, nested attributes, associations, persistence, validation, querying, etc. As Ernie put it, ActiveRecord does follow the single-responsibility principle, but only if you consider that responsibility to include everything that it does.
When I was starting off with Rails, I fell into the trap of thinking that all models had to inherit from ActiveRecord::Base. Adding a plain-old Ruby class to my app seemed to run against the grain of ActiveRecord, when in fact, I’ve found that starting with simple Ruby objects and adding components from ActiveRecord (e.g., ActiveRecord::Validations, ActiveRecord::Callbacks) independently as modules to be both more targeted and purposeful but also a better way of communicating to other developers how you envision that object operating within your domain. I like to think of models that inherit from ActiveRecord::Base as my primary objects and ActiveModel::Model classes and my simple Ruby classes or to be secondary and tertiary concerns in my object graph.
Ernie’s argument about being more picky about what to include or exclude from Rails’s large toolbox of sub-frameworks makes sense. Why add all that cruft to your application if you don’t even plan to use it (you could also just build a Sinatra app)? Yet in some ways, and especially for developers new to Rails who just want to build stuff, all the extra functionality that comes with the seemingly innocuous inheritance from ActiveRecord::Base is free, helps you get set up with a working website quickly and is reasonably performant, at least initially.
Those were just five of the many sessions at RailsConf 2014, covering not just technical topics but also more general interest stuff like career development. In particular, there were two panel discussions about the future of Rails jobs and what it takes to teach the next generation of great developers that generated some great conversation. Those panels featured well-known people in the Ruby/Rails community like Ben Orenstein of Thoughtbot and Jeff Casimir of Jumpstart Labs.