DHH says that explicit code favors novices and those with 'fresh eyes' on a problem while burdening experienced veteran programmers with bloat of boilerplate to read and to write.<p>I may as well come with fresh eyes for any code I wrote over a year ago. I'm reading _my own code_ with an eye to figure out how it was put together. I should be courteous to my future self and to anyone else who wants to read my code.<p>I pick up a new framework every year or two. I've been a full time software professional for over 16 years and I'm picking up new tools all the time. I prefer tools that tell me things explicitly so that I don't have the steep learning cliff of grokking the entire system in order to be able to work with one little part.<p>And isn't this hypocritical? So many people (probably DHH too?) gush about how easy Rails is to get started with, but here DHH argues that it's so much butter for the veteran who knows what all the implied magic is. Doesn't all that implied magic also leave the beginner kinda helpless? There's so much Rails tutorial with the attitude "here, do this, trust me, it'll work". And I've found that trust to be misplaced. The underlying implied design isn't actually what I wanted, and it's really hard to get it to do what I want because the convention is so baked in.<p>On the other hand, Go is a bit tediously boilerplate heavy. I'm still looking for the right balance.
Preferring implicit code is optimization for the first person writing the code while making it harder to understand, debug, and update.<p>When code is written only once and most of the cost comes afterward, that seems like an impossible choice to defend.<p>Why optimize for keystrokes? How much do keystrokes really cost you? Why optimize for conciseness, if it comes at the expense of readability?<p>I've maintained some PHP code bases with tons of implicitness (magic methods in ORMs and strings dictating object behavior, mostly) and some that had no implicitness. Guess which were more expensive for the client and painful for me?<p>Another example: I've recently been working on a C#/.NET code base, and I was wondering where the HTTP request is converted into a C# object (in this case, a JSON to object deserialization).<p>Guess where I had to go to figure it out? Google. And it wasn't easy to Google. I couldn't follow the code because of the layers of magic and configuration that controlled my code, rather than actual code. It was a huge waste of time and makes the code base completely opaque sometimes. It's a ball of yarn, and you don't know where it begins or where it ends.<p>Why is my code behaving strangely? Is it because I wrote it to do something a certain way? No, it's because this random config file (that I didn't know existed) is dictating behavior instead of the code itself.<p>I can't imagine how anyone working on large code bases with other people would want to do this. Yes, implicitness is more fun and beautiful at the beginning, but it becomes a nightmare after a short time for anyone other than the original coder.
Since this thread is somewhat rehashing the old arguments ("you write once, but read many times, therefore EIBTI"), it might be worth reading a more modern take on the topic by Aaron Turon of the Rust core team in his essay about the Rust language ergonomics initiative[1].<p>> But this, in my opinion, is a misdiagnosis of the problem, one that throws out the baby with the bathwater. The root issue is instead: how much information do you need to confidently understand what a particular line of code is doing, and how hard is that information to find? Let’s call this the reasoning footprint for a piece of code. The pitfalls above come from the reasoning footprint getting out of hand, rather than implicitness per se.<p>> Does readability then demand that we minimize the reasoning footprint? I don’t think so: make it too small, and code becomes hopelessly verbose, making it difficult to read by forcing too much information into view at all times. What we want is a sweet spot, where routine or easy to find details can be left out, but relevant or surprising information is kept front and center.<p>[1]: <a href="https://blog.rust-lang.org/2017/03/02/lang-ergonomics.html" rel="nofollow">https://blog.rust-lang.org/2017/03/02/lang-ergonomics.html</a>
Although this article never mentions the word, it's really about abstractions, not "implicitness" per se.<p>No one would say that if you have an almost-perfect abstraction, like a physics equation for fluid dynamics, that you should go to the effort of dropping down to the lower level (specifying what each molecule should do, in this case).<p>The problem is that this ORM example is an extremely leaky abstraction. Several of the inferences it is making are inferences that are not predictable if you don't already know how it will work. And they are inferences that matter.<p>Anyone who has ever used an ORM knows that it <i>seems</i> like magic, right up until you try to run some large query and it takes an eternity, and then you have to unpack it all and add all the explicit stuff to fix the problem that you "avoided" in the first place.<p>The real art of programming is in knowing what needs to be explicit and what can be implicit. I'd agree that if something can be made implicit <i>without mystifying callers</i>, it should be.
I would rather not leave myself a crime scene in my code that I have to figure out before I can do my work (especially when debugging!). I bounce around way too many systems in way too many languages to keep all the rules of every ORM and framework in my head.There's always a balance, but you can do really expressive things in the explicit. In the authors example of the Person, Ecto from Elxir doesn't favor magic happening for the has_many; you have to call out the validation AND relationships yourself. I think that's a good thing<p><pre><code> schema "people" do
field :first, :string, null: false
field :last, :string, null: false
belongs_to: :company, App.Company
has_many: :assignments, App.Assignment, foreign_key: :person_id
end
def create_changeset(attrs) do
%Person{}
|> Ecto.Changeset.cast(attrs, [:first, :last, :company_id])
|> Ecto.Changeset.validate_required([:first, :last, :company_id])
|> Ecto.Changeset.foreign_key_constraint(:company_id)
end
</code></pre>
And in the Assignment changesets you'd do the reverse for making sure the other sides' entities exist, or in the join table for many to manies.<p>I think the win readability wins later FAR outweigh the few validations I have to explicitly type out. A new person or someone under a deadline can quickly open one of the Ecto schemas, see all the relationships, see the validation being done, and see why stuff fails. And by turning it into a function you must explicitly call, it makes the data the most important thing and not the ORM itself. I could have called that function "parent_changeset" or "lawyer_changeset" if those made sense for what those functions did; they're not specially named. And then when you get that, you still need to actually apply the changes to have any effect.
Implicit code is indeed beautiful. DHH has also struck a great balance of implicit code remaining remarkably expressive with Ruby on Rails. It is the best example i have seen. But i would cite 2 shortcomings of implicit code:<p>- Implicit code increases the barrier of entry to new coders. This is counterintuitive as there is less overall code to learn. But implicit code often relies on auto-magic. Until you know the pieces of that magic, you are left confused. Case in point, as a web coder to find the file for `render user`.<p>- Implicit code works great until you need something to work differently than the implicit nature intends. This is because implicit code is also opinionated code. When your feature needs to fight against those opinions, you can expect to write a lot of highly explicit, ugly code to break that mold.<p>The slider of explicit-implicit will be talked about as long as code exists. Implicit systems result in less LoC, but you risk sacrificing expressiveness. For growing applications supported by growing teams, some of that time gain can slowly fade as your team gets younger and less experienced.
This article is ok as far as it goes, which isn't very far. Embracing implicitness can give benefits in the way that DHH describes. It can also lead to violations of your expectations.<p>Now, raise your hand if you honestly think that the answer is "embrace implicitness" or "embrace explicitness" full stop. In that context, it's worth noting that the Tao of Python was written well after the language was constructed, as a summary of the choices that were made. It was not originally intended to be a design document.<p>Sometimes implicit behavior is easy to understand, predictable, and saves lots of time. In those cases, it's good. In others, it borders on madness: <a href="http://stackoverflow.com/questions/12488705/incorrect-pluralizing-of-model-in-rails" rel="nofollow">http://stackoverflow.com/questions/12488705/incorrect-plural...</a><p>What determines the cases where one side of the tradeoff is good, and the other is bad? Show me cases where Rails does better than Django or some other framework and vice versa, and tell a story about how you identify those cases and judge whether the tradeoffs are worth it. That would be an article worth studying.
As always the answer is somewhere in the middle. For a core library that your company or even better everyone using the framework as you uses everyday implicitness is great. You invest a little bit upfront and it keeps paying off everyday. For one-off code you hardly ever touch you better be explicit. Of course there trick here is knowing which one you are writing. A good rule of thumb might be you start explicit and over time add an implicit layer on top while refactoring. With DHH's Active Record example that would have worked out nicely, since from the API perspective is really just reasonable defaults that could have been added once you realize that Active Record thing is actually being used in frequently.
Implicit code is great for frameworks. The kinds of things you have to do over and over again and a lot of the things Rails is great at. Create a database table, link it to an ORM, take params from a POST request, save them to that table...all that stuff should be pretty vanilla and absolutely yes, convention over configuration is amazing for that.<p>Explicit code is great for business level logic. In any business, and especially startups, things are changing so frequently that there's often very little in the code that could be set up as a convention and the accompanying "magic" that has to make it happen. Sales processes change weekly. Frontend features change daily. Reporting needs can change hourly.<p>In those environments you have to be able to quickly look at the code and see/know exactly what it is doing now and why so that you can be better able to make changes without introducing defects. Testing helps this a lot, but succinct, readable, explicit code makes life so much easier.<p>Implicit for frameworks; Explicit for business.
> Just on the database side, it implies that there’s a “people” table backing the “Person” record.<p>Plural table names are already pretty stupid, but using "people" vs "persons" even more so.<p>Implicitly having the table name match the entity after some case transformation is fine, but it should be more direct and not require an English dictionary to handle edge cases.
This is sort of a strawman, nobody on the explicit side of the debate wants people to write more boilerplate -- it's about how discoverable the code (and relatedly how many layers of indirection).<p><pre><code> class Person < ApplicationRecord
belongs_to :company
has_many :assignments
end
</code></pre>
For example what are the :company and :assignments symbols referring to? Why are they symbols? If they are models it would be better if they were required <i>explicitly</i> and that way I know where they are defined and can go read the code.
For every <i>explicit</i> thing in the code the programmer must pay attention to it when reading/understanding how the code works.<p>For every <i>implicit</i> thing in the code the programmer must have a mental model of what it is doing behind the scenes.<p>So one taxes the attention system of the programmer, and the other taxes her memory system.<p>Other things are both explicit <i>and</i> require a complex mental representation of state (Haskell), but the type system makes many classes of error impossible.<p>I think most people would agree with DHH's aesthetic when it comes to abstraction. What makes ActiveRecord's approach to abstraction get ugly in many cases is its coupling to the mutable state of the underlying database, which is not guaranteed to be in sync with the logic <i>impli</i>ed by the declarations, which may also have unforeseen timing side-effects.<p>Imagine the more typical scenario:<p><pre><code> class BusinessContract extends ActiveRecord::Base
belongs_to :company
belongs_to :client
belongs_to :sales_rep
acts_as_attachment
acts_as_state_machine
acts_as_versioned
before_save :notify_rep
after_save :update_salesforce_api
after_update :generate_pdf
end
</code></pre>
This class is now doing a great deal, and the user of the code needs to understand the time-dependent aspects of it and side-effects.
I enjoyed this article a lot because this is a problem I struggle with often when coding. And not just at a particular point in the lifecycle of a project, it comes up all the time.<p>Decisions like this are, in my case, heavily influenced by how much I <i>trust</i> the rest of the codebase. Having "magic" code on top of a rock solid abstraction feels nice and safe. When I debug / troubleshoot I don't feel the need to step into the function to look at its implementation one more time.<p>I am much more likely to be explicit, suck it up and write the boilerplate code once again in a messy codebase where the abstractions change all the time (whether I'm the one who wrote the sucky code or not). In the end, I feel like it comes down to how well your objects / concerns are designed. When the concerns are properly separated and your objects are solid, magic code is indeed beautiful and extremely expressive.
I wonder if maybe DHH is saying he prefers declarative code over imperative code.<p>I think it is often the case where one can argue that imperative programming is explicit, but in reality declarative syntax patterns are often easier to approach as a beginner and an expert.
As long as the implicit does the job it is fine. However when corner cases suddenly require the implicit to do something slightly different one is forced to dig into all the code that made the implicit work.
> It’s the same story with product values. If you say your product is “simple” or “easy”, you’re rarely saying much.<p>The descriptions for an incredible number of projects on GitHub lead off with something along the lines of "$PROJECT is a simple ...". My theory is that preemptively framing one's projects as "simple" might be a subconscious defence against accusations of triviality.
I agree with the author of this article. In general this is more about implicit functionality at the language level. Why is that much different from implicit functionality we get at the library level. In Java, when I use JPA, I am depending on massively loaded meaning behind a set of annotations... @OneToOne @ManyToOne, etc... at runtime you best know what all of those annotations are. I haven't really seen anyone that LOVES JDBC complain about tools like Hibernate/JPA. Yet it gives us lots of implicit meaning when we write this code that someone NOT familiar with JPA would be going crazy about - until they learn it.<p>To go a bit further and just state that suddenly it's NOT OK at the language level is kinda argumentless in my humble opinion.<p>And yes, there are degrees of acceptability. If JPA had an annotation like @OMGWTFBBQ and ActiveRecord supported something similar - it would be rejected. JPA and ActiveRecord do things like this, but much of the rest of these tools features outweigh their faults. I know for sure there are many Java frameworks that actually love it when you write your own annotation.<p>For example I use a @NoAuthentication annotation in Jax-RS. There is no standard annotation for that, yet in our company we all use it and know what it means. We mark this REST service as a service that anonymous users or authenticated users can call alike. This is because we favor a model of placing our authentication code inside the stack (in our case CXF) rather than adding code at EACH rest method. It's a very VERY implicit style, we can't look at and adjust our authentication model, but just the same, we SHOULDN'T be trusted to change our authentication model in each method.<p>Annotations are not exactly the same as implicits in other languages, but it's the closest example of something in Java that makes our lives much easier and hides a LOT of explicit info.
I think it's better to view this in terms of analyzing abstractions and their pros and cons, versus some vague notion of explicit vs implicit.<p>The more I learn, the more it seems an immense portion of high caliber software engineering is knowing how to approach and analyze very general questions related to abstractions, such as:<p>1. Given the current and expected future constraints, is this the best abstraction we can come up with?<p>2. Is the abstraction worth its cost? How heavily should we invest in using it, given how confident (or not) we are currently that it solves our problem well?<p>I think of abstractions as being everything from breaking an if condition into several local variables to splitting a large monolithic app into independent services. We could go further of course, but I think that gets the idea across.
There's a subjective balance to be struck between implicitness and explicitness. The example DHH gives are basically default parameters, which is a pretty reasonable amount of implicitness for me.<p>On the other end of the spectrum are things like Scala's implicit parameters, which I abhor.
I just published an article arguing the exact opposite a few days ago, funny timing:<p><a href="https://medium.com/@shazow/code-boilerplate-is-it-always-bad-934827efcfc7" rel="nofollow">https://medium.com/@shazow/code-boilerplate-is-it-always-bad...</a>
I really like scala's "implicit" syntax/feature, but sometimes I have trouble getting "how does this work...?"<p>hope scala (or intellij?) would get a "find all implicits related to this file/folder" and other things to help me around
You can still make good abstractions with explicit code, and remove most of boilerplates. But please at least be explicit about the fact you are using that abstraction.<p>One problem with Rails is that these abstractions are applied implicitly and burried deep in the framework. For DHH it's easy to know what it's doing behind the scene. Not for people who did not create these implicitly applied abstractions.<p>So.. Either you will have to learn ALL the Rails conventions, or you can create your own abstractions and use them explicitly. Also, it will be easy to put a breakpoint on the call site to your abstraction. Try that with Rails, you don't know what/where to put that breakpoint.
I don't think his example is great. Here is the equivalent in Django (in Python, a language that exposes 'explicit is better than implicit' as a core ideology):<p><pre><code> class Person(models.Model):
company = models.ForeignKey('company', related_name='people')
</code></pre>
How is this worse than the implicit version on the post? Basing it purely on a LOC argument is flawed as implicit doesn't always mean less code, and explicit doesn't always mean more code. In this example there is actually less code, you don't need a `has_many` at all (unless it's a m2m, then it's the same LOC).
I'm not familiar with RoR, but that explicit version is hard to read. However, I think the problem with implicitness is that when it becomes wide-spread, nobody can read the code with ease (i.e. everyone made their own implicit code, and it comes together poorly). I see nothing wrong with a little bit of magic, especially if it is something you use often.<p>Side note: the author has a short paragraph about "simple/easy" and "complicated/hard" products, which doesn't ring true with me. I would think most people would say their software product is "complicated, but easy to use".
When I develop in rails I tend to read the rails code a lot to understand what kind of implicit knowledge I was supposed to know. I suspect that veteran rails developers have done that a lot and also keep-up with framework upgrades when the assumptions are changing.<p>This is the tax we pay for being part of the DHH cult following (some against our will) :p
Implicit means that a lot of headspace is taken to remember all these details. I'd rather focus on building something cool, rails is not the end-goal.
DHH never fails to impress. As usual, he cuts to the core of his beliefs, and delivers sound argument for them.<p>Program with Ruby long enough and it really starts to feel like a religious experience. You feel like you can literally do anything. Things you can't do with it just don't seem important anymore.<p>Rails just takes Ruby and puts it on steroids.