Fri Jul 21, 2017
This is a growing and evolving list! If you see anything that I'm missing, email me
and I'll add it.
I’ve been spending a lot of time working on Buttondown lately and, as you might expect for a newsletter app, lots of this work revolves around dealing with email addresses.
In an effort to save myself and others some time, I’m trying to collate a list of usefully distinct and weird invalid and valid addresses for test harnesses and such. Below is what I have so far: it’s not a complete set, but it’s comprehensive enough to handle most of the weirdnesses out there.
Thu Jul 13, 2017
I’ve seen three different iterations of “code review culture”, and all of them have been positive with minor tweaks and changes. What follows is a general list of observations and advice:
“Finding and preventing bugs” is a secondary goal of code reviews, not a primary one.
You shouldn’t be relying on your code reviewers to find bugs; it’s nice to have an extra set of eyes who can point them out, but it’s your job as the implementer and tester to ensure correctness.
Everyone and everything should be subject to code reviews.
An atmosphere where senior engineers don’t have to go through CRs is bad: it implies poor things about your development hierarchy, like the idea that seniority means your code is immune to critique.
An atmosphere where critical bug fixes don’t have to go through CRs is bad: it implies you don’t have a testing/rollback framework set up to avoid the need to push unreviewed code.
Code reviews should not be more than X lines of code.
Anything more than X and diminishing returns set in. What
X is varies from language to language and context to context, but 200 is a pretty good number. (If your immediate reaction to this is “but most changes are more than 200 lines of code”, my immediate reaction is: nah.)
Like, they will be — someone (you!) is going to leave out a semicolon or mess up spacing, and that should be addressed in a review comment.
But if that’s the norm and not a occassional slip-up, then that’s a sign that you don’t have a documented code style. Whenever you or someone else comments we prefer null coalescing to ternaries here or whatever, that comment should go into a wiki and a lintfile so such issues can be automatically detected. The goal is to avoid work that can (and should) be automated so reviewers can focus on the important stuff.
(Automation is always preferable to documentation: it’s nice to have a central resource for these things, but it’s much nicer to have that central resource be your IDE or a pre-commit hook.)
Don’t be an asshole.
People have a wide range of how they internalize code reviews, and it is often hard to separate critique of code from critique of the coder. The best way to do this is to approach code reviews not as an adversarial process, but as a collaborative discovery of the best possible implementation.
A great way to do this that some folks gloss over: write positive comments in CRs. Positive reinforcement is just as helpful as constructive criticism: praising someone’s tests or the way they structured some tricky business logic doesn’t just make them feel good, it enforces that behavior for themselves and others.
The largest personal and institutional grievance folks have with code reviews is that they gum up the works and increase the time it takes to deploy code. Set team-wide practices on healthy expectations for when code should be reviewed and stick to them.
The woooooooorst thing to experience as a review-ee:
- You post some code.
- A reviewer makes comments A, B, and C.
- You address those comments and post the new code, asking for a new round of review.
- The same reviewer makes comments D, E, and F, all of which could have been made during the original review pass.
The woooooooorst thing to experience as a reviewer:
- Someone posts some code.
- You make comments A, B, C, and D.
- They address only A and D and post the new code, asking for another round of review.
Keep at it!
Code reviews can be long and they can be difficult but they are good for the long term health of your code base, just like building out a test suite or documentation.
Some further reading:
Thanks to Kelly Sutton, Iheanyi Ekechukwu, and Chad Little for reviewing this post. (And thanks to Michelle, Daniel, Andy, and countless others for reviewing my code over the years, and teaching me how to review well. )
Wed Jul 5, 2017
A great, thorough writeup on Django vs. Flask has been floating around the past few days. It’s a great technical breakdown, and I agree with the conclusion:
There’s an informal perception that Batteries included may mean a growing list of ill-maintained API’s that get hooked into every request. In the case of Django, everything works across the board. When an internal Django API changes, Django’s testsuites to break and the appropriate changes are made. So stuff integrates. This is something that’s harder to do when there’s a lot of packages from different authors who have to wait for fixes to be released in Flask’s ecosystem.
However, it also glosses over what is probably the strongest weapon in Django’s arsenal: Django Rest Framework. At this point, I essentially consider DRF a first-party package: it is so completely essential to my Python web development toolbox I can’t imagine working without it.
Having such a powerful, extensible approach to REST (and everything that entails: serialization, permissions, filtering…) is invaluable. Flask is great for toy projects, but for a modern application DRF is unreplaceable, and thus so is Django.
(Another important thing that Django does: it forces a certain level of structure and organization to your application. I used to think this was either unnecessarily or actively harmful: now, having seen my fair share of eldritch Python codebases, I know better.)
Mon Jul 3, 2017
Jasdev had a great post on moving too fast:
But, Agile almost entirely misses the other side of the coin: regimented rest. We can’t expect teams to knock out a monotonically increasing amount of story points, sprint after sprint. Sometimes, you have to move slower now to move faster later.
Somewhat adjacent to this is the best advice I’ve ever received as a developer:
Focus on being the highest order derivative you can be.
As an entry-level developer, you’re a simple function: you take time as an input and produce business value as an output.
Then as you mature as a developer, your work doesn’t directly produce business value: it produces an increase at the rate at which others can produce business value. Suddenly, you’re a first-order derivative.
And then you go further than that, becoming a second-order derivative: transcending from velocity to acceleration. Your work helps others increase the rate of change.
And so on and so forth.
I think this trajectory of increasing abstraction — stretching out your objectives over a longer and longer timespan — is natural, and good. Don’t worry about how to accomplish this week’s work; worry about how to accomplish this year’s work. Decoupling yourself from short-term inputs, like speed and time, is good: it’s a sign that your concerns are larger than the day-to-day.
Put more succinctly: agility isn’t about distance — it’s about nimbleness.
Or, to quote Murakami in What I Talk About When I Talk About Running:
Sometimes taking time is actually a shortcut.
Sun Jul 2, 2017
In my effort to throw a new coat of paint on this site, I was diving into Hugo’s internal templates to see how to improve their builtin pagination. It was invoked by calling
_internal/pagination.html, so I assumed the template was something along the lines of
Turns out all the internal templates are hard-coded in the Hugo library itself, in a file named template_embedded.go. Rather than filing a snarky comment on the wisdom of this approach, I thought it might be helpful to list them out, as it’s definitely annoying to read in the source file.
Sat Jul 1, 2017
Ben Sandofsky, the creator of Halide (and erstwhile lead of Twitter’s iOS/Mac apps) wrote a worthwhile piece about getting his new app’s size to a mere 11MB. There are a bunch of solid, universal pieces of advice (don’t create your own layout engine; compress and catalog assets), some trickier ones (avoid library bloat; eschew analytics), and a questionable kicker:
There really is one weird trick to lose size: focus on your customers.
I am skeptical about this last point in much the same way I am skeptical of folks saying the best way to lose weight is to eat healthy foods: well, sure, but what does that look like in practice?
Obviously focusing on your customers is very good, but I don’t think the truism of customer needs is paramount necessarily translates to slimming down your app is paramount.
This piece reminded me of an experiment Segment did last October, where they bought a random app and experimented with the app size to see how it affected downloads:
The first data point is insignificant (you can’t meaningfully compare download numbers between 2012 and 2016), but the latter three are pretty interesting, and suggest a linear (albeit tenuous) relationship between app size and install rate, to the tune of each megabyte costing the developer 0.45% of conversion rate.
That’s non-trivial! But if you’re an indie developer (or even just a pragmatic one), you have so much on your plate. You’ve got to build out features, you’ve got to iron out bugs, you’ve got to build out a marketing pipeline. All of these things are important, and some of these things might be solveable with a library or framework, which frees up your time to work on (potentially more important) other things.
Let’s take a completely arbitrary and abstract example of what I’m saying.
It’s four weeks before launch and you unexpectedly have a week of slack time. You can do one of these two things with your free time:
- Removing a 5MB library and replacing it with a home-brew version (bumping up install rate by, according to Segment, ~2.5%)
- Implementing a feature that bumps up install rate by 5%
The choice should be obvious, right?
Obviously nothing’s this clear cut: you’re never deciding between two things, you’re deciding between twenty things, and the time and value of these tasks are never quite as apparent as you’d like them to be.
But if you’re a small developer, every task you work on is a question of ROI: you only have so many battles you can pick. Sometimes fighting bloat is the right choice (and hey, it lets you write blog posts about how you fought bloat, which always makes for good marketing); sometimes it’s not.
I am skeptical that fighting app bloat is always the battle worth picking, but I think we can all agree that it’s at least a battle worth considering.
Sat Jul 1, 2017
This is gonna sound like a negative essay. Which it kinda is. But it's very much not a condemnation of Vue!
Vue is great. You should try Vue.
If I had written What Vue Doesn't Need it would be an incredibly long post! This is just things I wish were better about it.
I’ve been using Vue a lot recently on a few test projects, and most notably on Buttondown.
Buttondown’s frontend is 100% Vue: around twenty screens (which are themselves components) and around thirty miscellaneous components, tied together with Vuex and Vue-Router.
Overall, Vue is a really great tool, and the first word I’d use to describe it is pragmatic. Almost everything feels and acts sensible; there are tremendously few times that I am confused or surprised by how it works, which is legitimately novel (and wonderous) after the hours and days and weeks I’ve spent pulling my hair out dealing with random React and Webpack arcana.
Still, there are some bugaboos that I think arise from Vue’s current niche as a “lightweight” approach to creating a functional SPA, in much the same way that Flask carved out a niche as being a lightweight alternative to Django.
Sat Apr 29, 2017
Kelly Sutton wrote a great article about how TDD impacts software design:
Design Pressure is the little voice in the back of your head made manifest by a crappy test with too much setup. It’s a force that says when your tests become hard to write, you need to refactor your code.
I think this is absolutely correct, and touches on a larger concept that I’ve been internalizing over the past few months – that the best methods for evaluating the soundness of a codebase come from activities adjacent to the codebase.
(Or, to use fewer syllables, if it sucks to do something with the code, then the code probably sucks.)
Sat Apr 15, 2017
I’ve read two bad takes recently on how “faux native” technologies are considered harmful.
The first is alarmingly titled Electron is flash for the desktop:
Also all you web devs: Go learn C or Rust or something. Your program runs on a computer. Until you know how that computer works, you’re doomed. And until then get off my lawn shakes fist.
The second, a little less insufferable, is about an iOS developer’s experience with React Native:
Chances are that your finished RN app is going to feel like a not-quite-native iOS app and a not-quite-native Android app. Some people will have a bigger problem with this than others and most users of the general population won’t notice. But you do risk losing platform specific niceties that users will notice.
Both of these articles make some fair points, even if they’re not cogent overall.
Thu Mar 9, 2017
Here’s an old joke (or story, or fable, or whatever you want to call it) that I’ve heard some variation of a dozen times.
The new Jewish bride is making her first big dinner for her husband and tries her hand at her mother’s brisket recipe, cutting off the ends of the roast the way her mother always did. Hubby thinks the meat is delicious, but says, “Why do you cut off the ends — that’s the best part!” She answers, “That’s the way my mother always made it.”
The next week, they go to the old bubbie’s house, and she prepares the famous brisket recipe, again cutting off the ends. The young bride is sure she must be missing some vital information, so she askes her grandma why she cut off the ends. Grandma says, “Dahlink, that’s the only way it will fit in the pan!”