Explicit is better than Implicit

We spend much more time reading code than writing it, so it's better to aim for ease of comprehension, rather than attempt to abbreviate,  or hide complexity where it can't easily be found. While many frameworks available today do a very good job of taking care of the boring repetitive side of web development, sometimes they encourage hiding too much code behind magic, rather than just writing out explicitly what we want to happen.

Implicit behaviour in Rails

Rails has been criticised in the past for its magic - from magic finder methods to hidden dependencies in controllers and models. Many of these problems have been addressed in more recent versions (for example deprecating find_by_ methods), and there is a trade-off between a quick learning curve, and later problems caused by the subtle interactions with code you know nothing about.

The find_by_xx methods were implemented by catching calls with method_missing and dispatching to a newly created method, which wasn't great for performance, and meant that potentially any combination of columns could be allowed - a huge rise in complexity for not much gain as opposed to being explicit in calls to finders. They've now been taken out for Rails 5.

The acts_as pattern is another example of this - early in the development of Rails, plugins named acts_as_xx become popular. These gems have fallen out of favour somewhat, because  they abstract away so much functionality into a third party gem that it's harder to extend and harder to understand the flow of logic in your app. If your models acts_as a few different gems, which one includes the functionality involved in a certain method call?

Another example of implicit behaviour in rails are controller actions - before the action lots of setup occurs, modules are imported (not obvious in the controller file), context is set up, potentially several before actions are called (this is similar to the concept of middleware in Go), and then finally your controller action runs, at which point you don't even have to render, as rails will automatically render for you afterward. This makes it harder than it should be to trace the flow of execution in a rails app - if gems like cancan are requesting your models before you even hit your action in order to check authorisation, you might be puzzled by database calls you knew nothing about when an error occurs.

Middleware in Go

Middleware in Go suffers a similar problem, in that the behaviour is hidden by a string of function calls wrapping each other, and it can become hard to decipher exactly which code is involved in processing a request without visiting several different middleware packages to trace execution. Of course there are advantages to an implicit style - code is terser, actions which are performed before every request anyway no longer clutter up the handler code, but taken too far it can led to a series of handlers with little to nothing in them, wrapped in other handlers which respond to your request in different ways.

Taken too far, the notion of implicit behaviour and magic in frameworks can lead to application code which is hard to debug because all the complexity has moved out of the application and into supporting packages (third-party or otherwise), but it's still there, just hidden from view and scattered around all the dependencies of your app.  That's why in Fragmenta, although tasks like authentication, queries and view rendering are taken care of for your application, handler functions  explicitly query the database,  check authorisation, and render the view – using libraries as helpers, rather than letting them handle state completely. That's also why I prefer generating default code which can be adapted or replaced for simple CRUD actions - while generating code might seem anathema, it is often a good way to impose predictable structure on resources, while allowing for the inevitable requirement of customisation later on.


Discuss at Golang News