Uncategorized
Copypasta, you're the worst pasta
Copypasta. It’s the worst. “I need something like this code here, I’ll just drop it over there where I need it. Maybe change a few things.” Only you can prevent headdesks!
It’s not really possible, in my experience, to make it easier to use code through methods and functions than to just copy what you need and start changing it. No amount of encapsulation or patterns is easier than a pasteboard.
Perhaps, copypasta’s natural predator is a well-informed code review. There are tools, like flay, that can detect some kinds of code duplication.
But for the most part, it’s a battle of dilligence.
(Ed. I found this in my draft folder from four years ago. Copypasta; copypasta never changes.)
Through mocks and back
A problem with double/stub/mock libraries is that they don’t often fail in a total manner. They don’t snap like a pencil when they’re used improperly. Instead, when you use them unwisely, they lay in waiting. At an inopportune time, they leap out.
Change an internal API method name or argument list and your poorly conceived doubles will moan. Rearrange the relationship between classes, and your overly-specific stubs won’t work anymore.
At some point, I felt pretty handy with mocks. Then I wrote a bunch of brittle mocks and decided I needed to go back to square one. I’m through the “just avoid mocks” phase, and now I use them sparingly.
Favor a better API in the code under test, then hand-coded fakes, then stubbed out methods, before finally falling back to a mock. Someone, possibly yourself, will thank you later.
Does an unadvertised extension point even exist?
There was an extension point, but I missed it.
I was adding functionality to a class. I needed to add something that seemed a little different, but not too far afield, from what the existing code was doing. So I came up with a good name, wrote a method, and went about my day.
A few weeks later, trying to understand an obscure path through this particular class, I found the extension point I should have been using. On one hand, eureka! On the other hand, why didn’t I notice this in the first place?
Did I not consider the open/closed principle enough? Perhaps my “modification” sense should have tingled, sending me to create a new object to encapsulate the behavior with.
Was the extension point hidden by indirection? Perhaps the change going into an ActiveRecord model through me off; I was doing as you’d normally do in a model. I wasn’t expecting another layer of abstraction.
Were my changes too scattered amongst many files? I had modifications in a half-dozen files, plus their tests. It’s possible I was juggling too many things.
Probably it’s all of these things. Lesson learned: when I get to feeling clever and add a handy extension point to make the next person’s job easier, advertise that extension point and make it clear this is probably where they want to make their change.
The TTY demystified. Learn you an arcane computing history, terminals, shells, UNIX, and even more arcanery! Terminal emulators are about the most reliable, versatile tools in my not-so-modern computing toolkit. It’s nice to know a little more about how they work, besides “lots of magic ending in -TY”, e.g. teletypes, pseudo-terminals, session groups, etc.
Clinton Dreisbach: A favorite development tool: direnv. I’ve previously used direnv
to manage per-project environment variables. It’s easy to set up and use for this. I highly recommend it! But, I’d never thought of using it to define per-project shell aliases as Clinton does. Smart!
Turns out Ruby is great for scripting!
Earlier last year, I gave myself two challenges:
- write automation scripts in Ruby (instead of giving up on writing them in shell)
- use system debugging tools (strace, lsof, gdb, etc.) more often to figure out why programs are behaving some way
Of course, I was almost immediately stymied on the second one:
[code lang=text] sudo dtruss -t write ruby -e "puts 'hi!'" Password:
dtrace: failed to execute ruby: dtrace cannot control executables signed with restricted entitlements [/code]
dtruss
is the dtrace-powered macOS-equivalent of strace
. It is very cool when it works. But. It turns out Apple has a thing that protects users from code injection hijinks, which makes dtrace not work. You can turn it off but that requires hijinks of its own.
I did end up troubleshooting some production problems via strace
and lsof
. That was fun, very educational, and slightly helpful. Would do again.
I did not end up using gdb
to poke inside any Ruby programs. On the whole, this is probably for the better.
I was more successful in using Ruby as a gasp scripting language. I gave myself some principles for writing Ruby automation:
- only use core/standard library; no gem requires, no bundles, etc.
- thus, shell out to programs likely to be available, e.g.
curl
- if a script starts to get involved, add subcommands
- don’t worry about Ruby’s (weird-to-me) flags for emulating sed and awk; stick to the IRB-friendly stuff I’m used to
These were good principles.
At first I tried writing Ruby scripts as command suites via sub. sub
is a really cool idea, very easy to start with, makes discovery of functionality easy for others, and Just Works. You should try it some time!
That said, often I didn’t need anything fancy. Just run a few commands. Sometimes I even wrote those with bash!
But if I needed to do something less straightforward, I used template like this:
[code lang=text] #!/usr/bin/env ruby
HELP = <<-HELP test # Run the test suite test ci # Run test with CI options enabled test acceptance # Run acceptance tests (grab a coffee….) HELP
module Test module_function
def test
rspec spec
end
… more methods for each subcommand
end
if __FILE == $0 cmd = ARGV.first
case cmd when "ci" Test.ci
… a block for each subcommand
else Test.test end end [/code]
This was a good, friction-eliminating starting skeleton.
The template I settled on eliminated the friction of starting something new. I’d write down the subcommands or workflow I imagined I needed and get started. I wrote several scripts, delete or consolidated a few of them after a while, and still use a few of them daily.
If you’re using a “scripting” language to build apps and have never tried using it to “script” things I “recommend” you try it!
Tinkers are a quantity game, not a quality game
I spend too much time fretting about what to build my side projects and tinkers with. On the one hand, that’s because side projects and tinkers are precisely for playing with things I normally wouldn’t get a chance to use. On the other hand, it’s often dumb because the tinker isn’t about learning a new technology or language.
It’s about learning. And making stuff. Obsessing over the qualities of the build materials is besides the point. It’s not a Quality game, it’s a Quantity game.
Now if you’ll excuse me I need to officiate a nerd horserace between Rust, Elm, and Elixir.
On recent Mercedes-Benz dashboard designs
Mercedes (is it ok if I call you MB?), I think we need to talk. You’re doing great in Formula 1, congratulations on that! That said, you’ve gone in a weird direction with your passenger car dashboards. I suspect there are five different teams competing to win with these dashboards and I don’t think anyone, especially this car enthusiast, is winning overall.
Here’s your current entry-level SUV, the GLA. If my eye is correct, this is one of your more dated dash designs:
[caption id=“attachment_3827” align=“alignnone” width=“634”] All the buttons![/caption]
Back in the day, I think, you had someone on staff at MB whose primary job was to make sure your dashboards had at least 25 buttons on them. This was probably a challenging job before the advent of the in-car cellular phone. However, once those became common, that was 12 easy buttons if you just throw a dial pad onto the dash. And you did!
So it’s easy to identify this as an older design from the dozens (43) of buttons. But the age of this design also shows from the LCD. One, it’s somewhat small. Two, and more glaringly, you simply tacked the LCD onto the dashboard. What happened here? Did you run out of time?
I think you can do better. The eyeball vents are nice though!
Now let’s look at a slightly more modern, and much further upscale, design. Your AMG GT coupe:
[caption id=“attachment_3828” align=“alignnone” width=“673”] All the suede[/caption]
OK so you lost most of the buttons in favor of bigger, chunkier buttons. That’s good! You also made a little scoop in the dash for the LCD. That’s progress, but the placement still feels awkward. I know that’s where all your luxury car friends put the LCD now, but you’re so dominant in F1, maybe you can do better here too?
Can we take a moment to talk about the interactions a little? Your take on the rotary control is a little weird. You’ve got one, and it’s got a little hand rest on top of it. That seems good. But then the hand rest is also a touch interface for scribbling letters? Seems weird! I’ve never used that, but I’m a little skeptical.
Next, you’ve gone through some weird stuff with your shifters. You had a really lovely gated shifter on the S-class couple as long ago as the early 90’s! Lately you’ve tried steering column shifters, and now it seems you’ve settled on a soap-shaped chunk of metal that you move up and down to change directions and put it in park. I feel like you should give up the physical shifter thing and just go with (you’re gonna like this) more buttons.
My parting thought on the GT’s interior is this: width. Your designs impart a sense of tremendous girth in the dash, making the car feel bigger. We’ll come back to that immediately…
Finally, one of your most recent designs, the E-class sedan:
[caption id=“attachment_3829” align=“alignnone” width=“867”] All the pixels![/caption]
Again with a regal sense of width. Personally, I don’t like it. It makes your car seem like a giant sofa.
You are making great progress on reducing the number of buttons. Again, kudos.
OK, clearly you got a great deal on LCD panels. Plus, an almost equally good deal on eyeball vents. Good for you!
Also you put an analog clock on the dash. So that’s nice.
I don’t think we’re going to like this giant piece of software and glass thing for very long. Did you see Her? There are hardly any displays in it. All the computers are somehow inhabited by the characters, either by talking to them or interacting within a projection. Why take one of your classic instrument clusters, make that an LCD, and occasionally project information onto the windshield if the driver or passenger needs to see it there? Just a thought!
I’m a little split on the design of your wheel there. It’s nice that technically it’s a 3-spoke design but really, if you count, it’s 4 spokes. The lamest number of spokes. Perhaps with the split you were trying to add more negative space and perhaps evoke a very old, SL-like 2-spoke design? That’s a nice gesture, but I think you missed here. Surely you could engineer a straight-up 2-spoke wheel?
In summary:
- fewer buttons, less noticeable screens, more seamless interactions
- a few retro design elements (eyeball vents, analog clocks) are great, too many is too much
- reduce your five design teams (screens, buttons, wheels, interactions, A/C) down to two: driving interactions and auxiliary interactions
Hope that helps!
The least bad solution
Sometimes I look over the options and constraints to choose something suboptimal. I have to pick the least-bad solution.
I recently chose a least-bad way to write a test. In a Rails app, the most sensible thing to solve my problem was something like this:
[code lang=text] def propagate_from_child_to_parent model_parents.find_each(&:do_a_sideeffect) end [/code]
In the test, I ended up having to write this assertion
[code lang=text] expect_any_instance_of(ModelParents).to receive(:do_a_sideeffect) [/code]
This kind of stub and assertion is clearly a smell. But, consider the alternatives:
- stub out the child model object under so that
find_each
returns a stub object that I can make suredo_a_sideffect
is called on - try to hack around ActiveRecords associations so it returns the same object as I inject in my test
- seek out some other result of
do_a_sideeffect
that I could assert on
In the end, it felt like the shady mock+assertion was the best choice. Using that particular assertion says “slow down and pay attention, a special thing is happening here”. It’s not something I want to do every time, but it was the least bad solution in this context.
Wanted: state machines in the language
Our programming languages are often structured around the problem domain of compilers and the archaic (for most of us) task of converting things people understand to a thing the computer can execute.
Why don’t our languages have deeper support for the ways we reason about problem domains or the ways we struggle to reason. For example, why aren’t state machines and checking their sanity (or marking their unsoundness) a thing in pretty much any language?
The unhelpful answer is “because you can write a state machine in library code”. Which leads me to ask, why don’t we have popular state machine clones? Why is there no xUnit or Sinatra of state machines that is widely cloned to fresh and exciting languages?
The cynical answer is “because many programmers don’t want to think that hard”. The optimistic answer is that there’s room for someone to capture this problem space as well as xUnit did for programmer testing or Sinatra did for turning URL-like strings into method calls. You could be famous!
We’re all adults here, but we’re not all mind readers
My favorite advice on the topic of method visibility (i.e. public vs. private) comes from Python creator Guido van Rossum. It goes something like “we’re all adults here” and says it’s not really a necessary thing for compilers/runtimes to hide methods from specific callers. Don’t go mucking around in other object’s implementations. I still think that’s mostly right.
Except, coming up to speed on a new code base is vastly easier when there’s some delineation of the waterline between an object’s public API which it expects other objects to use and its private implementation which it does not. It tells me a) the private methods are open for change and refactoring and b) below the private “waterline”, don’t bother going any deeper when spelunking to figure out how this program works.
In a new or strange codebase, every little bit of tractability helps.
Here comes GraphQL
GraphQL is gaining purchase outside of the JavaScript communities and this seems like a pretty good thing. Shopify and GitHub have jumped on board. Absinthe (Elixir) and graphql-ruby have caught my attention, though I haven’t had an opportunity to tinker with them yet.
That said, I like that GraphQL (and JSON API) let service developers focus on exposing a specific data model and optimizing access to it rather than taking a side quest through REST API design. For application developers, building screens and interactions with the data they need defined inline seems like a big win for contextual understanding.
As ever, the risk of using any kind of mapping layer, whether its objects and relational data or JSON object graphs to downstream service calls, is creating a naive, one to one mapping that create awkwardness and inefficiency.
Refactor the cow paths
Ron Jeffries, Refactoring – Not on the backlog!
Simples! We take the next feature that we are asked to build, and instead of detouring around all the weeds and bushes, we take the time to clear a path through some of them. Maybe we detour around others. We improve the code where we work, and ignore the code where we don't have to work. We get a nice clean path for some of our work. Odds are, we'll visit this place again: that's how software development works.
Check out his drawings, telling the story of a project evolving from a clear lawn to one overwhelmed with brush. Once your project is overwhelmed with code slowing you down, don’t burn it down. Jeffries says we should instead use whatever work is next to do enabling refactorings to make the project work happens. Since locality is such a strong force in software, it’s likely that refactoring will help the next bit of project work. Repeat several times and a new golden path emerges through your software.
In other words, don’t reach for a new master plan when the effort to change your software goes up. Pave the cow paths through whatever work you’re doing!
Our current political Trolley Problem
As self-driving cars inch closer to a daily reality, the Trolley Problem seems to have entered our lexicon. In short, should a self-driving computer choose to avoid hitting a bunch of people and kill its single occupant as a result? Turns out people expect the car to protect the greater good second and their own skin first.
Maybe out our current political environment of unfettered gun violence, climate change, Trump-lead racism, Brexit-fueled xenophobia, and general apprehension about losing what we thought we’d earned are a kind of longer-term but still serious Trolley Problem. Would you vote to improve society at large even if it meant taking yourself down a ego/prestige/money notch?
Well when I put it that way, things seem pretty bleak!
I happened across an Alan Kay essay, Enlightened Imagination for Citizens, and it kinda helped me get through that bleakness. Some highlights:
In a raging flood, a man risks his life to save a swept away child, but two years earlier he voted against strengthening the levee whose breaching caused the flood. During an epidemic people work tirelessly to help the stricken, but ignored elementary sanitation processes that could have prevented the outbreak. More astoundingly, as many as 200,000 Americans die each year from diseases spread by their own doctors who have been ignoring elementary sanitation (including simply washing their hands when needed), but who then work diligently to try to save the patients they have infected. Studies show that about 80% of Americans are “highly concerned” about climate change, yet this percentage drops to less than 20% when the issue is combined with what it will cost to actually deal with these changes.
Regarding our inability to reason about dynamic systems:
One of the reasons the consequences were not imagined is that our human commonsense tends to think of “stability” as something static, whereas in systems it is a dynamic process that can be fragile to modest changes. One way to imagine “stability” is to take a bottle and turn it upside down. If it is gently poked, it will return to its “stable position”. But a slightly more forceful poke will topple it. It is still a system, but has moved into a new dynamic stability, one which will take much more work to restore than required to topple it.
On acting now instead of acquiring a perfect answer or solution:
When the costs of an imperfectly understood event are high or essentially irreversible, measures have to be taken even when perfect proofs are lacking. This idea is understood by most developed societies—and carried out in the form of levees and pumps, food and water stocks, etc.—but is nonetheless resisted by many of the voting public.
Perhaps the solution is to get ourselves representatives that excel at reasoning and legislation instead of politics and fundraising?
One of the reasons we are a republic with a democratic base is that the representatives can be selected to be “the best and the brightest” from the population as a whole (this was another early ideal for the great American experiment). We could argue that the current representatives are “all too representative”, but this is part of a slide in our political and social systems that needs to be shored up and improved. The idea of “national service” is now just a whisper, but it is what needs to be brought back into the forefront of what it means to be a citizen.
A few qualities of mature developers
What is technical leadership? Per Mature Developers, it's a lot of things. My favorites:
So one of the first and most important qualities of mature developers is they’re more often than not paying attention to what is going on around them. They’re deliberately taking their time to observe before proceeding (put succinctly as STOP; Stop, Take a breath, Observe, Proceed).
It is so hard for me to do the stop and breath part.
Sharing the [technical] vision with other involved parties not only serves as a perfect opportunity for practicing one’s skills to explain deeply technical terms and circumstances with non-technical people. It also serves the purpose to validate the vision in terms of relevance to business value and other aspects.
Assessing and understanding risks better puts them into a position where it’s also more likely they’ll actually take risks. Risks which, without the knowledge about business value and the bigger context, may look too big to be worthwhile. But not for mature developers who are able to see beyond the obvious risks and include more aspects into their judgement.
Managing risk, but not overmanaging it: also very difficult.
Previously: Thoughts on “Being a Senior Engineer”.
Why I blog in bursts
I write here in bursts. It confounds me as to what marks the beginning and end of those spikes. I have a few hunches:
- ambitions grow larger than my free time: it’s easier to hit publish on a self-contained thought than a connected series or magnum-opus essay
- intervention of life: work, vacation, various chores adults are expected to perform
- self-distraction: acting as a novelty junky rather than pushing one thing through to completion
- tweeting less: putting little thoughts into tweets means I’m driven to put slighly-not-little thoughts into blog posts
- reading less: reading interesting things drives me to (attempt to) write interesting things
- skipping record: I worry I’ve already had this thought and published it somewhere
Also sometimes I’m not quite sure how to end a thought like this and I wonder if I should worry about that and then I decide to let it slide.
Extra Ruby chaining, not that costly
A few folks suggested I try lazy enumerables to make my extremely chained style practical. I was curious about the actual costs of my style, so it’s time for lies and microbenchmarks! Turns out naively chaining a bunch of maps together isn’t very costly, so go with that to start.
Lazy came in much slower than consolidating the logic in one loop or chaining them without lazy. I thought, I must not have used lazy properly. Turns out, I’m probably showing that laziness isn’t well suited to iterating over collections without an early termination clause (e.g. a take, first, or find) and that for small collections (like an 87-line /etc/passwd), the cost of the lazy plumbing can noticeably outweigh the work done inside the loops. Thanks to Rein Heinrich for talking me to the bottom line!
One idea per line
Lately, I’m doing a weird thing when writing Ruby code. I’m trying to only put one idea or action per line. I’m not sure about it yet.
Here’s what a method to fetch item-y things might look like:
def fetch_items(options={})
limit = options.fetch(:limit)
timestamp = options.fetch(:timestamp)
paged_helper = PagedHelper
client = OurHttpClient
responses = paged_helper.
new(limit, timestamp).
fetch_pages { |params| client.get(params) }
responses.
map { |r| JSON.parse(r) }.
map { |h| ItemCollection.new(h) }.
map { |ic| ic.items }.
flatten
end
For the sake of comparison, here’s how I may have written that method a couple years ago:
def fetch_items(options={})
helper = PagedHelper.new(limit, timestamp)
responses = helper.fetch_pages { |params| OurHttpClient.get(params) }
responses.map { |r| ItemCollection.new(JSON.parse(r)).items }.flatten
end
I like that the pace of reading the first example is even. You don’t arrive upon some monster line of code that does a multiple things. You don’t have to unpack what’s happening in a situation where you’re calling f(g(h(some_args)))
. It makes moving lines of code around much simpler because each one is only dependent on what comes before, and not what happens inside. It’s a little easier to write a three-part method, which I really like.
But still, I hesitate. My methods end up about 50% longer. Breaking up the Enumerable
transformations into multiple loops instead of one loop doing a bunch of work is probably pretty slow. I have to come up with a lot of names (which is, I think a net good), some of which end up a little redundant.
I’ll let you know how it goes. It may not even survive code review, who knows!
Less fancy
Programming is easier when you know how to stop solving 100 problems with 1 fancy thing and solve 100 problems with 20 plain things.
How does a bomber outlast a JS library?
Ember is probably leading the JavaScript framework pack by supporting releases with security patches for slight more than a year. By comparison, there’s a cottage industry of garages restoring and updating old Porsche sports cars then selling them for ridiculous prices. The USAF (the same one, curiously, that is spending $1.5 trillion on a useless jet, somehow) is going to use their largest strategic bomber, the B-52, for one hundred years.
I’m always thinking about Greg Borenstein’s words when it comes to technology churn:
The constant churn of web technologies hobbles the creation of timeless learning materials and continuity of knowledge across generations.
We should try harder on this.