The One Tech Concept Everyone Should Learn
There is a mental model that has gained some traction recently called chestertonās fence. The moral of the story is that we should figure out why something exists before we change or remove it. This is a useful piece of wisdom. Of course, the next question that naturally follows is āHow?ā How do we ensure that we are only removing redundant fences? How do we ensure there isnāt a bull waiting to trample us on the other side?
The answer to this question is not embedded in the mental model. But I think one concept from the tech world can facilitate answering the question of āHow?ā. It is a concept worth understanding even if you consider yourself nontechnical and never plan to get technical.
That concept is called refactoring.
Envisioning how entire systems operate is a difficult thing to do ā perhaps near-impossible to do in totale. So processes of refactoring were developed to aid software developers in building external scaffolding around systems to verify, at key points, that the system is operating as intended. These specific processes ensure the behavior of a system wonāt change while the system is updated internally.
Refactoring: What Is? š
Refactoring code is often compared to editing a piece of writing. This is a useful analogy to get started:
When we write an essay or a blog post, we might start with a simple stream of consciousness to see what we come up with. Once there are enough words and ideas to play with, we might sort that stream into a coherent outline. Once structured, weāll flesh out the body with full paragraphs, smooth out transitions, and hopefully not add too many adverbs1. Then we edit. Once we have a real draft, weāll either wait a while before looking it over again or have someone else look at it for us. We hone our words to increase precision and convey the right meaning. Rinse, lather, repeat.
Writing code is, for the most part, quite similar to this.
The first priority is just to get a solution working at all and this might be accomplished in a stream-of-consciousness-like fashion. Outlining might be compared to designing and whiteboarding a solution; drawing out diagrams to represent how the system might look from a birdās eye view. Further fleshing may then include hashing out finer details2 and adding comments to the code. Then we refactor. We determine what levels of abstraction make the most sense: Should I wrap this bit of logic in its own function? Should this be an API call that others donāt have to worry about? Once code is moved to a (hopefully) more appropriate level, we verify the system still operates as expected. Rinse, lather, repeat.
As useful as these parallels are, there are details that get lost in translation. Key differences lurk between the process of remixing words on paper and yanking characters around a code editor. Upon closer inspection, these differences can be nontrivial. And within those differences lie potential solutions that our society sorely needs.
Differences: A Close-Up š
āPerfection is achieved not when there is nothing left to add, but when there is nothing left to take away.ā
~ Antoine de st. exupery; wind, sand, and stars; 1939
If you are curious, there are entire blog posts and books written about how refactor code. For the purposes of this post, here are some important highlights:
- Make it understandable and mind your levels: The main purpose is to improve the readability (or efficiency) of code by placing logic at appropriate levels of abstraction.3
- Donāt add new features now: The goal is to improve the function of the system, not to change the systemās behavior.
- Verbalize assumptions and verify them: The process includes adding automated tests to validate the codeās functionality.4
1. Make It Understandable and Mind Your Levels š
This step is almost identical between editing and refactoring ā minus engineering optimizations that can occur while refactoring. This visual, illustrating the evolution of technology, exemplifies the end goal and what the process should feel like beautifully:
The aim for both editing and refactoring is to improve clarity, coherence, and conciseness. Topics of editing such as word choice, correct grammar usage, transition management, jargon explanation, and the like all have familiar parallels in refactoring: Variable naming, correct syntax usage, module interaction management, commenting codeā¦
The one guiding principle Iāve come to rely on to make things more understandable is to answer the question: At what level should this live?3 The answer to this one question will guide you in making sense of multiple levels of your overall structure simultaneously whether youāre considering sentences nested in paragraphs in chapters, or functions inside of classes in modules.
2. Donāt Add New Features Now š
Both editing and refactoring almost always result in the changing and deletion of content. But while editing may include additions, many coders advise refraining from adding new features while refactoring. We are bad at multi-tasking, so just donāt do it. The act of adding, and potentially changing the behavior of the system, should be performed completely separately from changes and removals meant to improve the system itself.
3. Verbalize Assumptions and Verify Them š
Whether itās editing or refactoring, the goal is to get some idea across as clearly as possible to others. When you re-word your essay, youāre aiming to add precision to your message so that others will be more able to grasp your meaning. While refactoring, we want to increase the clarity of intent and crispness of logic in a codebase, while ensuring our solution still works and solves the original problem(s). We can reasonably be more confident our solution still works as expected when we follow something like the process below:
- Develop an understanding of the problem space and how the codebase solves the problem.
- Write code that verifies this understanding (called āunit testsā or āautomated testsā6).
- Make edits to the original codebase to increase the clarity of logic and intent (see 1. Make It Understandable above).
- Validate the newly written code against the verification code you wrote (by running the automated tests ā and hope they all āpassā).
- Repeat steps 1-4 as necessary.
Having your future self or others read and edit your writing can act similarly to this process. And it is conceivable that a skilled editorās process will mirror this portion of refactoring to the extent that it is possible ā it is hard to beat automated tests.
But it is worth emphasizing the two steps I think are often missing in many editing processes:
- Step 2 gets you to think about the codeās purpose and forces you to make it explicit (with the writing of automated tests).
- Step 4 validates that understanding against reality (with automated tests).
Remodeling Fences š
The process outlined above arguably revolves solely around verifying assumptions ā and preventing disaster. This is because systems are extremely complicated. Alice could make a single change that percolates throughout the entire system, wreaking havoc on all the users downstream, without even knowing it. This is especially true of details that have faded into the background of normalcy. Perhaps that change removed a feature fundamental to Bobās workflow ā but he didnāt even know it was fundamental until it was broken or gone: It had always just worked. Similarly, we tend not to think about how and why cultural norms, laws, and regulations shape the way we liveā¦Especially when they just work.
But this is a mistake and it is dangerous. We should get in the habit of making these norms explicit, to understand whatās going on under the hood, and verify that what weāve verbalized is accurate and in accordance with reality. We should not only be doing this when we build web applications or machine learning pipelines, but in society at large. The processes of refactoring software could guide and inform the creation of a framework that helps us better innovate culture, society, and government.7
We must not forget that if we canāt describe why the fence is there, we wonāt know what we will unleash when we try to take it down.
With regard to societal systems, this post is best read in conjunction with [[2022-01-28-forget-pos]].
-
āMost adverbs are unnecessary.ā ~ [[on-writing-well]]Ā ā©
-
For example, say you wrote a dummy timestamp function to return the same time every time you called it in order to force uniform behavior. Once the desired function around that timestamp is complete, you might then replace it with the current time to actually get the desired functionality.Ā ā©
-
For more on levels: townhouse; how to move between them: [[compressed-explanation]]; and the process of building them up and breaking down: [[break-chunk]].Ā ā©Ā ā©2
-
There may be developers who disagree with this definition. True, testing is not always assumed in the steps of refactoring. But I would equate not using tests to not inspecting the other side of Chestertonās fence. Say hello to Toro for me.Ā ā©
-
āAutomated testingā in software development is often compared to ādouble-entry bookkeepingā in accounting.Ā ā©
-
Occasionally you will hear someone say they want to start society over from scratch. But even in software, experienced developers will vehemently warn against this. If we canāt reasonably expect to start a technical project over with less effort than it takes to refactor it, how do you expect to do so with something as vast as society?Ā ā©