The Thinker in The Gates of Hell
File copied from Wikipedia under the Creative Commons Attribution 2.0 Generic license. [source]
Programs are written in code. Code is described by programming languages. If you understand a programming language, given enough time, you can read a program’s code and understand how it works. There is no magic in programming.
Yet, when you’re new to programming, it feels like there’s magic everywhere. Programs do weird things and it’s hard to understand why. Even after you’ve mastered one area of an application, the rest of it may still feel like a looming shadow of uncertainty.
I’d like to use this blog post to make the following three arguments:
- We shouldn’t let fear of the unknown stop ourselves from understanding code
- Organizations are learnable systems we can understand and work with too
- Stifling fear and being inquisitive are important parts of gaining seniority
To help provide context on those points, I’m going to bring in an example from an unusual source: a book on religious thought.
I’m midway through reading A Letter in the Scroll by the late great Rabbi Lord Jonathan Sacks. It’s a magnificent read regardless of your personal spirituality -I’m an atheist myself, raised Jewish- and I’d highly recommend it in that context. The chapter titled “The Idea of Man” was particularly interesting to me as a developer in how it links ancient humans’ lack of power over nature with how they developed worldviews:
There have been cultures—ancient Greece is the supreme example—that saw the world in terms of vast impersonal forces… Seen in this perspective, the forces that govern the world are … not addressed to us.
Contrast that with its description of Abrahamic religions:
The Hebrew Bible represents a radical alternative… We can do more than react to stimuli; we can contemplate alternatives and choose between them.
We can imagine and act on the basis of our imagination.
In other words, some ancient civilizations saw us as pawns at the whim of gods, while Abrahamic religions see us as players at the table, though still beholden to an influencing higher power. (I’m slightly manipulating the chapter’s intent and ignoring many interpretations of the monotheistic God to make a more narrow point. For a real religious discussion, go read the book!)
There is a parallel between those different worldviews and how professionals -software developers in particular- grow in their careers. Early stage developers often don’t fully understand the APIs, functions, and other system they interact with. They use the concept of black boxes to skip understanding those systems deeply. No shade against black boxes — without them, the cognitive complexity of any task would be boundless. I often use the concept in my day-to-day programming.
One downside of thinking in terms of black boxes, though, is that it can get you in the habit of avoiding understanding systems. One of the most common mistake I see developers make is avoiding trying to understand a new area of code adjacent to what they’re working on. Instead of understanding the system as a whole, they isolate their changes to just the areas they understand. They then are at risk of missing crucial context contained in those surrounding areas.
As an extreme example, I once came across a section of a project where a moderately complex set of code logic had convergently evolved in almost identical lines of code across several functions. That violation of DRY wouldn’t be so bad on its own — except the functions were directly calling each other, making all but one of the sets redundant!
When asked why they hadn’t moved or reused existing code, each developer who’d contributed to the duplication indicated they hadn’t looked deeper into the call stack. Each had treated other areas within the same section of the codebase as a black box.
Black boxes are a useful technique for simplifying designs in thought, but make sure you don’t use it to the point of not understanding the systems at play. Remember you’re a human and can do more than react to stimuli. You can investigate and learn inner workings. There are no natural forces or magical impossibilities in programming.
From reading “The Idea of Man”, I think I can finally articulate a succinct, easily explainable perspective on the differences between software developer job titles. In order:
- Junior developers: are getting their start understanding the “forces of nature” around them
- Mid-range developers: are competent, perhaps confident, in manipulating those forces of nature
- Senior developers: have seen through the facade and can work with the systems behind those forces of nature
- Staff developers: are competent, perhaps confident, in manipulating those systems
To truly master your domain, you need to be comfortable treating areas as a black box or diving in questioningly as needed. I don’t know if this is a particularly accurate or scalable judge of seniority. But I’m going to try using it as a frame of reference for a while.
Organizations of people are systems, too. Rules governing their behavior and processes exist that are generally followed.
When you first join an organization, though, it can be hard to feel that it’s an understandable system. Assignments are given to you from the forces that be -a black box of decision-making- and are expected to improve the product in a pre-ordained way. Who are you, a cog in the machine, to question the powers around you?
The ability for developers to be inquisitive on assignments and general organizational decision-making around themselves is crucial for at least two reasons:
- Without understanding goals, you’re unlikely to execute tasks correctly
- It’s exceedingly difficult to positively influence the organization around you if you never question, let alone attempt to manipulate, it
Tasks are not given in isolation: there is always underlying context and tasks generally support a larger team or business goal. Developers all too often aim to to satisfy the exact criteria given to them instead of thinking deeply on the best way to satisfy the backing context.
If a ticket is written requesting a cache to improve performance calling a slow endpoint, it’s tempting to take that request and dive in to implement a cache without further analysis. But maybe the endpoint’s performance could be improved, eliminating the need for a cache? Maybe you should use an existing technique such as edge caching instead of (re-)implementing your own? Is that endpoint even called in a hot path, and if it is, can it be lazy-loaded to not be a blocker?
There is an old joke that developers would prefer hours of trial and error over a few minutes reading documentation. The same holds true for understanding requirements. A few minutes thinking deeply about the right solution can save hours implementing the wrong one.
Systems of people are just that: systems of people. A system has well-defined inputs and outputs. A team makes decisions because it’s been given a set of goals to reach and resources to work with. Each person on that team is motivated by their own career incentives, goals to reach, and past experiences.
When a team process gives a result you don’t agree with -a feature backlogged; an investigation cut short; whatever- that doesn’t have to be the end of the story. Talk to the people involved in the process to learn why they came to that conclusion. Do they have information you’re missing, or vice versa? Ask around. Apply Five Whys. If you think you have information that wasn’t considered, counterpoint with it. I’ve seen far too many silly decisions persist because a team member heard an incorrect decision and didn’t question it.
Treating systems you don’t understand as “black boxes” is a useful tool, but shouldn’t be overused to the point of avoiding understanding what’s around you. Remember that there is no magic in programming: it’s all governed by logical systems. “Systems” means both the programming systems -APIs, frameworks, IDEs, languages, etc.- and organizational -data, OKRs, people, teams, etc.-.
I would much rather work with a developer who’s pretty good at coding and pretty good at providing feedback on my pull requests, RFCs, and team planning than a developer who is amazing at coding and quiet on the rest.
What about you?