Tag: Are Right A Lot

The Herpes Of Version Control

The Herpes Of Version Control

I love git. Truly. Once I got over the fear of learning something new and dove in, everything else paled in comparison. Maybe it’s just because I was a math major and directed acyclic graphs are cool. Maybe it’s because it everything about it makes sense (kinda like Python in that respect). Maybe because it’s the foundation behind open source software development sites like Github.

But this post is not about how much I love git, or why you should use it over the alternatives. It’s about one particular feature that drives me crazy at times, and that’s tags. Yes those littles things you use to mark important places along the development tree. They’re quick and easy to make (especially by CI tools that vomit them out daily), and really useful to have around. But the darn things are nearly impossible to get rid of.

It’s not that the implementation is confusing or doesn’t have logical justification (it does). But the distributed nature of git means they spread like wildfire, and are incredibly difficult to delete across all cloned repositories. You think you’ve gotten old ones deleted, and then some poor developer who hasn’t cleaned up his repo (or even worse, the local copy of a repo on your CI server) pushes and they all come back. Argh.

For a guy who is as anal-retentive as they come about keeping his repos tidy, tags are just the worst.

Ain’t No User Here

Ain’t No User Here

Here’s a new one. I was debugging a problem with a web server that hadn’t been used in a while that was allowing users to log in, but failing to perform a number of other functions (gotta love an API that happily returns a 200 OK even though it’s clearly not working because there’s no data in the response). No one could think of anything that had changed, but clearly something had.

After some spelunking through logs, I found some DB permissions errors. Obviously some DB calls were working, so that was odd, as I knew credentials were only specified in one place.

Another hour of research later, I discovered that MySQL views run as the user who created them, not the user who queries them. This actually makes sense for use cases where you want to give controlled read access to parts of the DB to a less-privileged user. Logically then, if that user is deleted, the view fails to run. And in this situation that’s exactly what happened.

There’s a number of lessons to be learned from this situation:

  1. A user probably should fail to delete if it owns other objects in the database. Otherwise unexpected side-effects occur.
  2. Accurate error messaging is essential to debugging. When trying to run the view, I got a simple “user does not have permission” error, which told me nothing about the underlying problem, which meant another hour or two of research. This goes for APIs especially. Please know when to use a 4XX vs. 5XX error especially, and even better learn the subtle differences between 502, 503, and 504, and when each should be used.
  3. Don’t try to diagnose problems on Friday afternoons before holiday weekends. You’re asking for trouble.
Do The Right Thing

Do The Right Thing

I love it when a programming language behaves in an unsurprising and helpful way. This is Python in a nutshell.

Consider the following situation I came across yesterday. I needed to pair off two lists. Naturally, Python has a built-in function for this:

x = [1, 2, 3]
y = ['a', 'b', 'c']
zip(x, y)
# [(1, 'a'), (2, 'b'), (3, 'c')]

But what happens if the lists are different lengths (as they were in my case)? Python does the logical thing, and stops with the shorter list:

x = [1, 2, 3]
y = ['a', 'b', 'c', 'd', 'e', 'f']
zip(x, y)
# [(1, 'a'), (2, 'b'), (3, 'c')]

This is exactly the behavior I needed, no special case handling required. Woot!

Gimme That Foot Gun

Gimme That Foot Gun

The past couple of days I wrote about the dangers of providing too much functionality. In a fit of cognitive dissonance I now want to contradict myself and demand dangerous power when it suits my needs.

I’ve been working the past week to get a particularly gnarly application running in a set of docker containers. There are over a dozen services, plus a Rabbit queue and a database. Many of the services do not handle database connection failures in a robust way. During my testing I wanted a simple way to ensure they waited a bit before trying to connect, as the database container needs a minute or so to seed itself and get ready for connections.

Unfortunately,docker-compose does not have any form of manual startup delay feature. This is by design, as the Docker team (rightly) argues that having services intolerant to connection failure is a bad thing. However, it’s frustrating to not have the power to do the wrong thing in the short term.

Then again, it turns out it wasn’t too tough to augment my compose file with a depends_on clause that includes a health check, which is a more reliable solution anyways.

User Error Isn’t

User Error Isn’t

It happens to every developer at some point. You spend a bunch of time trying to determine the source of an urgent bug, only to find out the problem was caused by action or inaction of the user. It’s easy in that moment to be frustrated, but may I suggest a more circumspect path? It behooves a thoughtful developer in these moments to consider if the source of the user error was caused by poor design. Perhaps the button wasn’t in an obvious place, or the side effects of the command were not obvious, or the name of the feature didn’t describe the outcome. In many (even most) cases, user error is preventable with good design.

Yes it’s difficult, and perhaps impossible in some situations. But it is the high calling of the software engineer to design products in a way that a user almost subconsciously knows how to operate without error. Viewing design with this attitude saves a lot of frustration.

It might seem this is a counter argument to Not Even Mostly, but actually they go hand in hand. A user must be carefully listened to and considered, but sometimes they must also be guided into the features they really need, versus what they say they want. Do that hard work up front, and you’re more likely to have a system that minimizes the potential for mistakes.

Also, go read The Design Of Everyday Things.

Turn And Face The Strange

Turn And Face The Strange

Writing requirements is hard. Rarely is enough thought put into them, and even when a lot of time is spent, gaps are inevitable. Thus the most important feature to build into a system’s architecture at the beginning is the ability to change. Without that, all other features will quickly become obsolete.

Every. Single. Time.

Every. Single. Time.

Want to know what happens if you don’t follow the Boy Scout Rule of software development? This.

That being said, it can be really easy to get into a vicious cycle of constant rewriting and never releasing. We’ll talk about that tomorrow.

Missing The Mark

Missing The Mark

Yesterday I made a broad claim about the applicability of various subjects to the task of software development. Today I want to give a specific example from the realm of theology.

The notion of sin is foundational to a number of world religions. Without belaboring the details or arguing the validity of any one definition, what cannot be disputed is that humans have been wrestling for a long time with their own limitations. Why is it that none of us are able to live up to our own ideals, let alone the ideals of a faith tradition? Why must it be that “nobody’s perfect”? To quote Saint Paul,

I do not understand my own actions. For I do not do what I want, but I do the very thing I hate . . . I have the desire to do what is right, but not the ability to carry it out.

Great stories confront us with the fickle imperfections of human nature as well. Is not human frailty and the conflict that arises from it the very foundation of an epic tale? Nothing proves this truth quite like “fiction”.

What does this have to do with software? Let me repeat myself: software is made by humans, and the results are subject to their constraints. That means you can expect both errors of omission and commission, and both unintentional mistakes (e.g. a developer fails to adequately review code) and deliberate laziness, cheating, pride, etc. One certainly hopes for few instances of the latter, but they do happen. It’s not just time you have to plan for.

A process must be designed to recover from its own imperfect implementation, else it is doomed to fail.

The Holy Grail

The Holy Grail

As of today, I do not think there is a better general purpose language in existence than Python. With few exceptions, it just feels “right”, and without fail writing it makes me happy. Here’s just a couple of my favorite features:

Clean syntax

Python eschews block delimiters in favor of strict indentation rules, which both keeps the code more readable, and forces good formatting. It also requires a minimum of special characters, enhancing readability even further. I work with Perl extensively in my day job, and just look at how much grosser it looks than the equivalent Python:

if ($enemy == 'klingon') {
   firePhasers();
}
else {
   openHailingFrequencies();
}
if enemy == 'klingon':
    firePhasers()
else:
    openHailingFrequencies()
Implementational flexibility

I’m not sure that’s actually a word, but what I mean to say is that Python is a solid choice no matter the design approach you want to take. Writing a simple procedural script? Python does that. Taking an object-oriented approach? Python does that too. Want to dabble with a functional style? Oh yeah, Python’s got your back.

While the purists might say Python isn’t the ideal choice for any of those scenarios, I say it’s probably good enough, and in the real world I’d rather be an expert in one language than a novice in three.

Extensive PACKAGE library

I’m continually amazed at how much functionality is included in Python’s standard library. It makes writing code so much quicker. And if a feature isn’t in a default package, there is a massive ecosystem of third party libraries.

In my next couple of posts I’ll share a few of my favorite of these packages.

To Be Fair

Python isn’t perfect, and there are a couple things I don’t care for. For example, take its ternary syntax:

action = 'punch' if captain == 'Kirk' else 'talk'

I’m generally a fan of ternary operators because of their concision (which is a word I just learned), but the ordering of this construction feels awkward. See, I can be unbiased!