Posts
Space background with direction sign

My Brief History of Programming Principles

May 2, 2021

It's not hard to debunk cliched phrases like Don't repeat yourself. It's clear they don't apply across all possible scenarios, however, they've still guided and shaped me as a developer.

I want to discuss a few programming principles that have influenced me, in chronological order:

DRY - Don't repeat yourself

My earliest memory of programming principles comes from university. I remember being introduced to functions and how they enable code-reuse without copy-paste. DRY became my mantra as I hunted down code duplication candidates to be extracted as utility functions.

Needless to say, I spent the next few years obsessively optimizing my codebases much too early. However, over time I became better at recognizing the areas where rigorous application of DRY has diminishing returns (CSS, UI styling) vs. where it's absolutely critical (data access layer).

PEP20 - The Zen of Python

There should be one-- and preferably only one --obvious way to do it.

I learned to code in Python. Unlike Ruby and Perl, Python isn't shy about spelling out best practices. I've since moved on to other languages but I still carry the mottos from The Zen of Python with me. Like "Explicit is better than implicit". I highly recommend giving them a read to inspire you how to improve your code.

Needless to say, I spent the following years painstakingly converting for-loops into more readable List Comprehensions 😉.

Unix philosophy

“Write programs [or functions] that do one thing and do it well.” And “Expect the output of every program [or function] to be the input of another, as yet unknown, program.”

For those of us working in academia in the early 2010s, cloud computing was out of reach. We ran all of our analyses on our own infrastructure. This meant gluing a lot of scripts together with Bash. Spending my days inside Unix terminals certainly rubbed off on me. I came to discover and appreciate interoperable and single-purpose tools like ls, cat, grep with friends. A good rule of thumb I brought with me from this time was:

If I have to use “and” when describing what a function/module/program does it’s time to break it up.

As I build React-based interfaces I still aim to design the ideal component API before hooking it up to any specific business logic that is inferred from my API or global state. This keeps them for getting tightly coupled to what the current business logic demands.

Optimizing for testability

I vividly remember my discovery of unit testing. Still at my academic job, I obsessed about reaching 100% test coverage which I could brag about in my open source project READMEs.

Reaching that goal meant I needed to account for testability already in the initial software architecture. No more spaghetti code! No more raw MySQL queries! I was pushed to get familiar with advanced patterns like dependency injection. This usually brought nice side effects that made my solutions more robust and generalizable.

I used testability to:

  • ...decide between possible implementations, always choosing the one that made my code more testable.
  • ...assess overall codebase health, basing it on how hard it was to implement new tests.

Poor testability is a code smell, however, I've never come across a testable yet crappy codebase.

The focus on testability also pushed me to think about separation of concern before anything else. Needless to say, I spent the next few years prematurely optimizing my codebases, separating code into neat layers and implementing plugin systems that never grew beyond a default add-on.

AHA programming - Avoid Hasty Abstractions

Duplication is far cheaper than the wrong abstraction (Sandi Metz)

After leaving academia, I started working for an agency. We often focused on quick turnaround to get our products in the hands of users early. This meant that testing became only one of many concerns. Instead I developed a new appreciation for maintainable code that could be successfully handed over to new maintainers.

I also made the switch to JavaScript development which introduced me to Kent C. Dodds. He has since had a big influence on my way of coding. I use his AHA programming principles to avoid my main nemesis: premature optimization.

AHA programming comes with several important realizations:

  1. Whenever we extract code into a function, we also create a new layer of abstraction. Doing it too early contributes to avoidable technical debt.
  2. A shared function also couples two (potentially) separate modules by both depending on the new abstraction. This complicates future refactors.
  3. Finally, abstractions tend to live a life of their own. Even when they are no longer used, they tend to stick around. So many time, I've worried that removing deprecated functions will cause more harm than keeping them around just in case.

Optimizing for deleteability

I've come a long way from when I itched to re-write old codebases, convinced that this time I would get it right. One important realization I carry with me is that I need to write code to understand how it should be structured.

Given this belief, I also accept that code I write today eventually will be rewritten, removed, or refactored. The best way to serve future maintainers (including myself) is to make my code easy to delete. Be humble. Whoever maintains the code a year year from now will know best what to do with it.

An important part of writing deleteable code is to lean into co-location. Related pieces of code should be placed as close to each other as possible. This goes for styles, documentation, and unit and integration tests.

Conclusion

That's pretty much where I am right now. I keep collecting new principles I pick up along the way to fill in pieces of the software development puzzle. What are some programming principles that have inspired and influenced you most? Let me know @robinandeer!


Further reading