I have improved the code samples to make them closer to valid, working Python.—jbrains
At some point, you know I had to write this article. Let me state something clearly from the beginning.
In spite of the title, I use mocks freely and happily.
I do not intend with this article to join those who either shun mocks or appear to shun mocks only to praise them. I plan to share a simple example of using mocks as a stepping stone towards a potentially more suitable design, but you must not interpret this as a message against mock objects.
There. I feel better now. I trust you to carry my message the way I intended it. Do not betray that trust.
I asked my Twitter community which HTTP client to use. Certainly not HTTParty, because cool people don’t use HTTParty anymore. One takes these risks when one falls out of the loop with the latest and greatest tools.
“Use Faraday”, they said. “It’s the best”, they said.
Everything went fine until I needed to follow a redirect. What happened from that point forward could form the basis of a solid one-act play. I’ll spare you the horror and send you directly to the punch line.
At Øredev 2013, I asked managers to tell me what makes their job difficult. I spoke to one manager who told me an all-too-common story about facing immense pressure from the board of directors to deliver more features more often and similar pressure from the programmers to give them more time to refactor. More important than the details of this situation, I’ve heard dozens of stories like these over the years, and while one single piece of advice can’t possibly address every significant aspect of these situations, I have found that most of the people who tell me these stories identify with what I’d like to share with you.
Warning. You almost certainly have more to deal with in your exact situation than this advice can cover. You might need some more direct help. I can offer that to you, but for now, read on.
Legacy code imposes an unbounded, inexact tax on the cost of all future features. This explains why I run events like Legacy Code Retreat and why I write about improving the design of software systems. This further explains why I’m willing to accept the risk of people labeling me as an “over-engineerer”. What they label “over-engineering”, I consider managing risk. As part of managing risk, I like to test-drive changes to legacy code, and implicit dependencies almost always get in the way of starting. I’m going through this right now, working with Octopress, the blogging software that I use here.
Let me state this clearly: I really like Octopress. If you intend to interpret what follows as me attacking Octopress, then stop reading and go away. Thank you.
I love having tricky conversations on Twitter. I know a lot of you don’t, because you can’t express yourself clearly in 140 characters or less, but I love it for precisely that reason. The constraint makes it easy to voice objections, which makes assumptions visible and encourages us to challenge them. You’d be amazed at the things that people object to – issues that you thought were settled a long time ago, which you take for granted, at least within certain of your social circles.
This Twitter conversation reminded me of one of my own, long-standing assumptions… the one upon which I based the name of this website.
@ecomba@jbrains The more I hear this notion expressed in summary, the harder it is for me to keep liking it.
You owe it to yourself to read the entire conversation, so click on the Tweet’s timestamp and read further. Allow me to summarise: Letting your code tell you where it wants to go does not mean conducting a seance, speaking to the dead.
In case that conversation link has long-since died, let me state very clearly that when I write code, it might look like I use a Ouija board, except that I know that I’m controlling it. I’m looking for signals in my code to indicate design problems, then I decide which problems to address right now, then decide what to do about them. Some signals hit me more strongly than others. Over time I have developed an awareness of and a sensitivity to an ever-growing number of different signals that indicate potential design problems.
I just find it more fun to say that my code tells me where it wants to go. Please don’t interpret this too literally.
I routinely work with programmers who feel uncomfortable with tiny methods and tiny classes, in spite of all the benefits of tiny methods and tiny classes:
easier to test
easier to read
easier to understand
easier to build correctly
easier to compose into working systems
The programmers who don’t like them cite these problems most often:
“I can’t find stuff!”
“I have to click too much to see what’s happening!”
This article by Kevin Rutherford reminded me of this issue. While Kevin extols the virtues of tiny methods, he doesn’t address this phenomenon, although he admits to running into it himself. I wanted to share what I do with him, but it doesn’t fit well into a comment. When I hear programmers complain about these things, I offer the following two responses to them.
Another “demystifying” article. Great! I know… bear with me.
Of the SOLID principles, the Dependency Inversion Principle remains somewhat misunderstood. I’d like to help with that. I hope you’ll ask questions and challenge these ideas, so that I can improve this little article.
I learned the Dependency Inversion Principle from Bob Martin’s article, the salient part of which states:
High level modules should not depend upon low level modules. Both should depend upon abstractions.
Abstractions should not depend upon details. Details should depend upon abstractions.
I’ve noticed that I spend the vast majority of my time introducing abstractions and inverting dependencies (abstractions that use concrete things) when rescuing legacy code or adding behavior to legacy systems. Sometimes I feel like I don’t know any other useful trick than invert this dependency. One could make a drinking game out of it.
Even so, not enough people understand and apply DIP. Perhaps if I share a few examples or equivalent formulations, they will.
I apologise for the pompous title; I wrote it just for fun. If this “demystifies” anything, I’ll consider that a coincidence.
Of the SOLID principles, the most pompous-sounding is the one named for a person: Barbara Liskov. I don’t label her pompous – I never knew her – but of those five principles, only LSP has a common formulation that looks like the kind of mathematics so many programmers like to avoid, while others flock to.
Let q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T.
Or, if you prefer:
Subtypes must not change any supertype’s significant behavior. Here, “significant behavior” means behavior upon which clients of those objects expressly depend.
If you like the language of contracts, then you might prefer this formulation:
Read on →
I read about Design by Contract first, didn’t understand it, then read about Test-Driven Development and began to think of TDD as a kind of DbC by example. Accordingly, I have respect for both practices and have even combined them on projects. Moreover, “Integrated Tests are a Scam” amounts to little more than Isolating Layers by Contract.
When a Vigil program is executed, Vigil itself will monitor all oaths (implorations and swears) that have been made. If an oath is broken, the offending function (the caller in the case of implore and the callee in the case of swear) will be duly punished.